1/*
2 * Copyright (c) 2010 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "netlogon.h"
37#include <nameser.h>
38
39static OM_uint32
40_netlogon_encode_dns_string(OM_uint32 *minor_status,
41                            const gss_buffer_t str,
42                            gss_buffer_t buffer)
43{
44    int ret;
45
46    memset(buffer->value, 0, buffer->length);
47
48    ret = ns_name_compress((const char *)str->value,
49                           (uint8_t *)buffer->value, buffer->length,
50                           NULL, NULL);
51    if (ret < 0) {
52        *minor_status = errno;
53        return GSS_S_FAILURE;
54    }
55
56    buffer->length = ret;
57
58    *minor_status = 0;
59    return GSS_S_COMPLETE;
60}
61
62static OM_uint32
63_netlogon_make_initial_auth_message(OM_uint32 *minor_status,
64                                    gssnetlogon_ctx ctx,
65                                    gss_buffer_t output_token)
66{
67    uint32_t flags = 0;
68#define MAX_NL_NAMES    5
69    gss_buffer_desc names[MAX_NL_NAMES];
70    uint8_t comp_names[3][MAXHOSTNAMELEN * 2];
71    size_t n = 0, i __attribute__((__unused__)) = 0 , len;
72    OM_uint32 ret;
73    uint8_t *p;
74
75    if (ctx->TargetName->NetbiosName.length) {
76        flags |= NL_FLAG_NETBIOS_DOMAIN_NAME;
77        names[n] = ctx->TargetName->NetbiosName; /* OEM encoding */
78        names[n].length++;
79        n++;
80    }
81    if (ctx->SourceName->NetbiosName.length) {
82        flags |= NL_FLAG_NETBIOS_COMPUTER_NAME;
83        names[n] = ctx->SourceName->NetbiosName; /* OEM encoding */
84        names[n].length++;
85        n++;
86    }
87    if (ctx->TargetName->DnsName.length) {
88        flags |= NL_FLAG_DNS_DOMAIN_NAME;
89        names[n].value = comp_names[i++];
90        names[n].length = MAXHOSTNAMELEN * 2;
91        ret = _netlogon_encode_dns_string(minor_status,
92                                          &ctx->TargetName->DnsName,
93                                          &names[n]);
94        if (GSS_ERROR(ret))
95            return ret;
96        n++;
97    }
98    if (ctx->SourceName->DnsName.length) {
99        flags |= NL_FLAG_DNS_HOST_NAME;
100        names[n].value = comp_names[i++];
101        names[n].length = MAXHOSTNAMELEN * 2;
102        ret = _netlogon_encode_dns_string(minor_status,
103                                          &ctx->SourceName->DnsName,
104                                          &names[n]);
105        if (GSS_ERROR(ret))
106            return ret;
107        n++;
108    }
109    if (ctx->SourceName->NetbiosName.length) {
110        flags |= NL_FLAG_UTF8_COMPUTER_NAME;
111        names[n].value = comp_names[i++];
112        names[n].length = MAXHOSTNAMELEN * 2;
113        ret = _netlogon_encode_dns_string(minor_status,
114                                          &ctx->SourceName->NetbiosName,
115                                          &names[n]);
116        if (GSS_ERROR(ret))
117            return ret;
118        n++;
119    }
120
121    for (i = 0, len = NL_AUTH_MESSAGE_LENGTH; i < n; i++) {
122        len += names[i].length;
123    }
124
125    output_token->value = malloc(len);
126    if (output_token->value == NULL) {
127        *minor_status = ENOMEM;
128        return GSS_S_FAILURE;
129    }
130
131    p = (uint8_t *)output_token->value;
132    _gss_mg_encode_le_uint32(NL_NEGOTIATE_REQUEST_MESSAGE, p);
133    _gss_mg_encode_le_uint32(flags, p + 4);
134    p += 8;
135
136    for (i = 0; i < n; i++) {
137        assert(names[i].length != 0);
138        assert(((char *)names[i].value)[names[i].length - 1] == '\0');
139        memcpy(p, names[i].value, names[i].length);
140        p += names[i].length;
141    }
142
143    output_token->length = len;
144    assert(p == (uint8_t *)output_token->value + len);
145
146    *minor_status = 0;
147    return GSS_S_CONTINUE_NEEDED;
148}
149
150static OM_uint32
151_netlogon_read_initial_auth_message(OM_uint32 *minor_status,
152                                    gssnetlogon_ctx ctx,
153                                    const gss_buffer_t input_token)
154{
155    NL_AUTH_MESSAGE msg;
156    const uint8_t *p = (const uint8_t *)input_token->value;
157
158    if (ctx->State != NL_AUTH_NEGOTIATE) {
159        *minor_status = EINVAL;
160        return GSS_S_FAILURE;
161    }
162
163    if (input_token->length < NL_AUTH_MESSAGE_LENGTH)
164        return GSS_S_DEFECTIVE_TOKEN;
165
166    _gss_mg_decode_le_uint32(&p[0], &msg.MessageType);
167    _gss_mg_decode_le_uint32(&p[4], &msg.Flags);
168
169    if (msg.MessageType != NL_NEGOTIATE_RESPONSE_MESSAGE ||
170        msg.Flags != 0)
171        return GSS_S_DEFECTIVE_TOKEN;
172
173    ctx->State = NL_AUTH_ESTABLISHED;
174
175    *minor_status = 0;
176    return GSS_S_COMPLETE;
177}
178
179static OM_uint32
180_netlogon_alloc_context(OM_uint32 *minor_status,
181                        gssnetlogon_ctx *pContext)
182{
183    gssnetlogon_ctx ctx;
184
185    ctx = (gssnetlogon_ctx)calloc(1, sizeof(*ctx));
186    if (ctx == NULL) {
187        *minor_status = ENOMEM;
188        return GSS_S_FAILURE;
189    }
190
191    ctx->State = NL_AUTH_NEGOTIATE;
192    ctx->LocallyInitiated = 1;
193    ctx->MessageBlockSize = 1;
194
195    HEIMDAL_MUTEX_init(&ctx->Mutex);
196
197    *pContext = ctx;
198
199    return GSS_S_COMPLETE;
200}
201
202OM_uint32
203_netlogon_init_sec_context(OM_uint32 * minor_status,
204			   const gss_cred_id_t initiator_cred_handle,
205			   gss_ctx_id_t * context_handle,
206			   const gss_name_t target_name,
207			   const gss_OID mech_type,
208			   OM_uint32 req_flags,
209			   OM_uint32 time_req,
210			   const gss_channel_bindings_t input_chan_bindings,
211			   const gss_buffer_t input_token,
212			   gss_OID * actual_mech_type,
213			   gss_buffer_t output_token,
214			   OM_uint32 * ret_flags,
215			   OM_uint32 * time_rec)
216{
217    const gssnetlogon_cred cred = (const gssnetlogon_cred)initiator_cred_handle;
218    gssnetlogon_ctx ctx = (gssnetlogon_ctx)*context_handle;
219    const gssnetlogon_name target = (const gssnetlogon_name)target_name;
220    OM_uint32 ret;
221
222    *minor_status = 0;
223
224    output_token->value = NULL;
225    output_token->length = 0;
226
227    /* Validate arguments */
228    if (cred == NULL)
229        return GSS_S_NO_CRED;
230    else if (target == NULL)
231        return GSS_S_BAD_NAME;
232
233    if (ctx == NULL) {
234        if (input_token->length != 0)
235            return GSS_S_DEFECTIVE_TOKEN;
236
237        ret = _netlogon_alloc_context(minor_status, &ctx);
238        if (GSS_ERROR(ret))
239            goto cleanup;
240
241        HEIMDAL_MUTEX_lock(&ctx->Mutex);
242        *context_handle = (gss_ctx_id_t)ctx;
243
244	ctx->GssFlags = req_flags & (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
245				     GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG |
246				     GSS_C_INTEG_FLAG | GSS_C_DCE_STYLE);
247        ctx->SignatureAlgorithm = cred->SignatureAlgorithm;
248        ctx->SealAlgorithm = cred->SealAlgorithm;
249
250        ret = _netlogon_duplicate_name(minor_status, (gss_name_t)cred->Name,
251                                       (gss_name_t *)&ctx->SourceName);
252        if (GSS_ERROR(ret))
253            goto cleanup;
254
255        ret = _netlogon_duplicate_name(minor_status, (gss_name_t)target,
256                                       (gss_name_t *)&ctx->TargetName);
257        if (GSS_ERROR(ret))
258            goto cleanup;
259
260        memcpy(ctx->SessionKey, cred->SessionKey, sizeof(cred->SessionKey));
261
262        ret = _netlogon_make_initial_auth_message(minor_status, ctx,
263                                                  output_token);
264        if (GSS_ERROR(ret))
265            goto cleanup;
266    } else {
267        HEIMDAL_MUTEX_lock(&ctx->Mutex);
268        ret = _netlogon_read_initial_auth_message(minor_status, ctx,
269                                                  input_token);
270    }
271
272    if (ret_flags != NULL)
273	*ret_flags = ctx->GssFlags;
274    if (time_rec != NULL)
275	*time_rec = GSS_C_INDEFINITE;
276    if (actual_mech_type != NULL)
277	*actual_mech_type = GSS_NETLOGON_MECHANISM;
278
279cleanup:
280    HEIMDAL_MUTEX_unlock(&ctx->Mutex);
281
282    if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
283        OM_uint32 tmp;
284        _netlogon_delete_sec_context(&tmp, context_handle, NULL);
285    }
286
287    return ret;
288}
289
290