1 /* $NetBSD: init_sec_context.c,v 1.4 2023/06/19 21:41:43 christos Exp $ */
2
3 /*
4 * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * 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 "ntlm.h"
37
38 static int
from_file(const char * fn,const char * target_domain,char ** domainp,char ** usernamep,struct ntlm_buf * key)39 from_file(const char *fn, const char *target_domain,
40 char **domainp, char **usernamep, struct ntlm_buf *key)
41 {
42 char *str, buf[1024];
43 FILE *f;
44
45 *domainp = NULL;
46
47 f = fopen(fn, "r");
48 if (f == NULL)
49 return ENOENT;
50 rk_cloexec_file(f);
51
52 while (fgets(buf, sizeof(buf), f) != NULL) {
53 char *d, *u, *p;
54 buf[strcspn(buf, "\r\n")] = '\0';
55 if (buf[0] == '#')
56 continue;
57 str = NULL;
58 d = strtok_r(buf, ":", &str);
59 free(*domainp);
60 *domainp = NULL;
61 if (!d)
62 continue;
63 if (d && target_domain != NULL && strcasecmp(target_domain, d) != 0)
64 continue;
65 *domainp = strdup(d);
66 if (*domainp == NULL)
67 return ENOMEM;
68 u = strtok_r(NULL, ":", &str);
69 p = strtok_r(NULL, ":", &str);
70 if (u == NULL || p == NULL)
71 continue;
72
73 *usernamep = strdup(u);
74 if (*usernamep == NULL)
75 return ENOMEM;
76
77 heim_ntlm_nt_key(p, key);
78
79 memset_s(buf, sizeof(buf), 0, sizeof(buf));
80 fclose(f);
81 return 0;
82 }
83 memset_s(buf, sizeof(buf), 0, sizeof(buf));
84 fclose(f);
85 return ENOENT;
86 }
87
88 static int
get_user_file(const ntlm_name target_name,char ** domainp,char ** usernamep,struct ntlm_buf * key)89 get_user_file(const ntlm_name target_name,
90 char **domainp, char **usernamep, struct ntlm_buf *key)
91 {
92 const char *domain;
93 const char *fn;
94
95 *domainp = NULL;
96
97 if (issuid())
98 return ENOENT;
99
100 domain = target_name != NULL ? target_name->domain : NULL;
101
102 fn = getenv("NTLM_USER_FILE");
103 if (fn == NULL)
104 return ENOENT;
105 if (from_file(fn, domain, domainp, usernamep, key) == 0)
106 return 0;
107
108 return ENOENT;
109 }
110
111 /*
112 * Pick up the ntlm cred from the default krb5 credential cache.
113 */
114
115 static int
get_user_ccache(const ntlm_name name,char ** domainp,char ** usernamep,struct ntlm_buf * key)116 get_user_ccache(const ntlm_name name, char **domainp, char **usernamep, struct ntlm_buf *key)
117 {
118 krb5_context context = NULL;
119 krb5_principal client;
120 krb5_ccache id = NULL;
121 krb5_error_code ret;
122 char *confname;
123 krb5_data data;
124 int aret;
125
126 *domainp = NULL;
127 *usernamep = NULL;
128 krb5_data_zero(&data);
129 key->length = 0;
130 key->data = NULL;
131
132 ret = krb5_init_context(&context);
133 if (ret)
134 return ret;
135
136 ret = krb5_cc_default(context, &id);
137 if (ret)
138 goto out;
139
140 ret = krb5_cc_get_principal(context, id, &client);
141 if (ret)
142 goto out;
143
144 ret = krb5_unparse_name_flags(context, client,
145 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
146 usernamep);
147 krb5_free_principal(context, client);
148 if (ret)
149 goto out;
150
151 if (name != NULL) {
152 *domainp = strdup(name->domain);
153 } else {
154 krb5_data data_domain;
155
156 krb5_data_zero(&data_domain);
157 ret = krb5_cc_get_config(context, id, NULL, "default-ntlm-domain",
158 &data_domain);
159 if (ret)
160 goto out;
161
162 *domainp = strndup(data_domain.data, data_domain.length);
163 krb5_data_free(&data_domain);
164 }
165
166 if (*domainp == NULL) {
167 ret = krb5_enomem(context);
168 goto out;
169 }
170
171 aret = asprintf(&confname, "ntlm-key-%s", *domainp);
172 if (aret == -1) {
173 ret = krb5_enomem(context);
174 goto out;
175 }
176
177 ret = krb5_cc_get_config(context, id, NULL, confname, &data);
178 if (ret)
179 goto out;
180
181 key->data = malloc(data.length);
182 if (key->data == NULL) {
183 ret = ENOMEM;
184 goto out;
185 }
186 key->length = data.length;
187 memcpy(key->data, data.data, data.length);
188
189 out:
190 krb5_data_free(&data);
191 if (id)
192 krb5_cc_close(context, id);
193
194 krb5_free_context(context);
195
196 return ret;
197 }
198
199 int
_gss_ntlm_get_user_cred(const ntlm_name target_name,ntlm_cred * rcred)200 _gss_ntlm_get_user_cred(const ntlm_name target_name,
201 ntlm_cred *rcred)
202 {
203 ntlm_cred cred;
204 int ret;
205
206 cred = calloc(1, sizeof(*cred));
207 if (cred == NULL)
208 return ENOMEM;
209
210 ret = get_user_file(target_name,
211 &cred->domain, &cred->username, &cred->key);
212 if (ret)
213 ret = get_user_ccache(target_name,
214 &cred->domain, &cred->username, &cred->key);
215 if (ret) {
216 free(cred);
217 return ret;
218 }
219
220 *rcred = cred;
221
222 return ret;
223 }
224
225 static int
_gss_copy_cred(ntlm_cred from,ntlm_cred * to)226 _gss_copy_cred(ntlm_cred from, ntlm_cred *to)
227 {
228 *to = calloc(1, sizeof(**to));
229 if (*to == NULL)
230 return ENOMEM;
231 (*to)->username = strdup(from->username);
232 if ((*to)->username == NULL) {
233 free(*to);
234 return ENOMEM;
235 }
236 (*to)->domain = strdup(from->domain);
237 if ((*to)->domain == NULL) {
238 free((*to)->username);
239 free(*to);
240 return ENOMEM;
241 }
242 (*to)->key.data = malloc(from->key.length);
243 if ((*to)->key.data == NULL) {
244 free((*to)->domain);
245 free((*to)->username);
246 free(*to);
247 return ENOMEM;
248 }
249 memcpy((*to)->key.data, from->key.data, from->key.length);
250 (*to)->key.length = from->key.length;
251
252 return 0;
253 }
254
255 OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_init_sec_context(OM_uint32 * minor_status,gss_const_cred_id_t initiator_cred_handle,gss_ctx_id_t * context_handle,gss_const_name_t target_name,const gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,const gss_channel_bindings_t input_chan_bindings,const gss_buffer_t input_token,gss_OID * actual_mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)256 _gss_ntlm_init_sec_context
257 (OM_uint32 * minor_status,
258 gss_const_cred_id_t initiator_cred_handle,
259 gss_ctx_id_t * context_handle,
260 gss_const_name_t target_name,
261 const gss_OID mech_type,
262 OM_uint32 req_flags,
263 OM_uint32 time_req,
264 const gss_channel_bindings_t input_chan_bindings,
265 const gss_buffer_t input_token,
266 gss_OID * actual_mech_type,
267 gss_buffer_t output_token,
268 OM_uint32 * ret_flags,
269 OM_uint32 * time_rec
270 )
271 {
272 ntlm_ctx ctx;
273 ntlm_name name = (ntlm_name)target_name;
274
275 *minor_status = 0;
276
277 if (ret_flags)
278 *ret_flags = 0;
279 if (time_rec)
280 *time_rec = 0;
281 if (actual_mech_type)
282 *actual_mech_type = GSS_C_NO_OID;
283
284 if (*context_handle == GSS_C_NO_CONTEXT) {
285 struct ntlm_type1 type1;
286 struct ntlm_buf data;
287 uint32_t flags = 0;
288 int ret;
289
290 ctx = calloc(1, sizeof(*ctx));
291 if (ctx == NULL) {
292 *minor_status = EINVAL;
293 return GSS_S_FAILURE;
294 }
295 *context_handle = (gss_ctx_id_t)ctx;
296
297 if (initiator_cred_handle != GSS_C_NO_CREDENTIAL) {
298 ntlm_cred cred = (ntlm_cred)initiator_cred_handle;
299 ret = _gss_copy_cred(cred, &ctx->client);
300 } else
301 ret = _gss_ntlm_get_user_cred(name, &ctx->client);
302
303 if (ret) {
304 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
305 *minor_status = ret;
306 return GSS_S_FAILURE;
307 }
308
309 if (req_flags & GSS_C_CONF_FLAG)
310 flags |= NTLM_NEG_SEAL;
311 if (req_flags & GSS_C_INTEG_FLAG)
312 flags |= NTLM_NEG_SIGN;
313 else
314 flags |= NTLM_NEG_ALWAYS_SIGN;
315
316 flags |= NTLM_NEG_UNICODE;
317 flags |= NTLM_NEG_NTLM;
318 flags |= NTLM_NEG_NTLM2_SESSION;
319 flags |= NTLM_NEG_KEYEX;
320
321 memset(&type1, 0, sizeof(type1));
322
323 type1.flags = flags;
324 type1.domain = name->domain;
325 type1.hostname = NULL;
326 type1.os[0] = 0;
327 type1.os[1] = 0;
328
329 ret = heim_ntlm_encode_type1(&type1, &data);
330 if (ret) {
331 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
332 *minor_status = ret;
333 return GSS_S_FAILURE;
334 }
335
336 output_token->value = data.data;
337 output_token->length = data.length;
338
339 return GSS_S_CONTINUE_NEEDED;
340 } else {
341 krb5_error_code ret;
342 struct ntlm_type2 type2;
343 struct ntlm_type3 type3;
344 struct ntlm_buf data;
345
346 ctx = (ntlm_ctx)*context_handle;
347
348 data.data = input_token->value;
349 data.length = input_token->length;
350
351 ret = heim_ntlm_decode_type2(&data, &type2);
352 if (ret) {
353 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
354 *minor_status = ret;
355 return GSS_S_FAILURE;
356 }
357
358 ctx->flags = type2.flags;
359
360 /* XXX check that type2.targetinfo matches `target_name´ */
361 /* XXX check verify targetinfo buffer */
362
363 memset(&type3, 0, sizeof(type3));
364
365 type3.username = ctx->client->username;
366 type3.flags = type2.flags;
367 type3.targetname = type2.targetname;
368 type3.ws = rk_UNCONST("workstation");
369
370 /*
371 * NTLM Version 1 if no targetinfo buffer.
372 */
373
374 if (1 || type2.targetinfo.length == 0) {
375 struct ntlm_buf sessionkey;
376
377 if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
378 unsigned char nonce[8];
379
380 if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
381 _gss_ntlm_delete_sec_context(minor_status,
382 context_handle, NULL);
383 *minor_status = EINVAL;
384 return GSS_S_FAILURE;
385 }
386
387 ret = heim_ntlm_calculate_ntlm2_sess(nonce,
388 type2.challenge,
389 ctx->client->key.data,
390 &type3.lm,
391 &type3.ntlm);
392 } else {
393 ret = heim_ntlm_calculate_ntlm1(ctx->client->key.data,
394 ctx->client->key.length,
395 type2.challenge,
396 &type3.ntlm);
397
398 }
399 if (ret) {
400 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
401 *minor_status = ret;
402 return GSS_S_FAILURE;
403 }
404
405 ret = heim_ntlm_build_ntlm1_master(ctx->client->key.data,
406 ctx->client->key.length,
407 &sessionkey,
408 &type3.sessionkey);
409 if (ret) {
410 if (type3.lm.data)
411 free(type3.lm.data);
412 if (type3.ntlm.data)
413 free(type3.ntlm.data);
414 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
415 *minor_status = ret;
416 return GSS_S_FAILURE;
417 }
418
419 ret = krb5_data_copy(&ctx->sessionkey,
420 sessionkey.data, sessionkey.length);
421 free(sessionkey.data);
422 if (ret) {
423 if (type3.lm.data)
424 free(type3.lm.data);
425 if (type3.ntlm.data)
426 free(type3.ntlm.data);
427 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
428 *minor_status = ret;
429 return GSS_S_FAILURE;
430 }
431 ctx->status |= STATUS_SESSIONKEY;
432
433 } else {
434 struct ntlm_buf sessionkey;
435 unsigned char ntlmv2[16];
436 struct ntlm_targetinfo ti;
437
438 /* verify infotarget */
439
440 ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
441 if(ret) {
442 _gss_ntlm_delete_sec_context(minor_status,
443 context_handle, NULL);
444 *minor_status = ret;
445 return GSS_S_FAILURE;
446 }
447
448 if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
449 _gss_ntlm_delete_sec_context(minor_status,
450 context_handle, NULL);
451 *minor_status = EINVAL;
452 return GSS_S_FAILURE;
453 }
454
455 ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
456 ctx->client->key.length,
457 ctx->client->username,
458 name->domain,
459 type2.challenge,
460 &type2.targetinfo,
461 ntlmv2,
462 &type3.ntlm);
463 if (ret) {
464 _gss_ntlm_delete_sec_context(minor_status,
465 context_handle, NULL);
466 *minor_status = ret;
467 return GSS_S_FAILURE;
468 }
469
470 ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
471 &sessionkey,
472 &type3.sessionkey);
473 memset_s(ntlmv2, sizeof(ntlmv2), 0, sizeof(ntlmv2));
474 if (ret) {
475 _gss_ntlm_delete_sec_context(minor_status,
476 context_handle, NULL);
477 *minor_status = ret;
478 return GSS_S_FAILURE;
479 }
480
481 ctx->flags |= NTLM_NEG_NTLM2_SESSION;
482
483 ret = krb5_data_copy(&ctx->sessionkey,
484 sessionkey.data, sessionkey.length);
485 free(sessionkey.data);
486 if (ret) {
487 _gss_ntlm_delete_sec_context(minor_status,
488 context_handle, NULL);
489 *minor_status = ret;
490 return GSS_S_FAILURE;
491 }
492 }
493
494 if (ctx->flags & NTLM_NEG_NTLM2_SESSION) {
495 ctx->status |= STATUS_SESSIONKEY;
496 _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
497 ctx->sessionkey.data,
498 ctx->sessionkey.length);
499 _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
500 ctx->sessionkey.data,
501 ctx->sessionkey.length);
502 } else {
503 ctx->status |= STATUS_SESSIONKEY;
504 RC4_set_key(&ctx->u.v1.crypto_recv.key,
505 ctx->sessionkey.length,
506 ctx->sessionkey.data);
507 RC4_set_key(&ctx->u.v1.crypto_send.key,
508 ctx->sessionkey.length,
509 ctx->sessionkey.data);
510 }
511
512
513
514 ret = heim_ntlm_encode_type3(&type3, &data, NULL);
515 free(type3.sessionkey.data);
516 if (type3.lm.data)
517 free(type3.lm.data);
518 if (type3.ntlm.data)
519 free(type3.ntlm.data);
520 if (ret) {
521 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
522 *minor_status = ret;
523 return GSS_S_FAILURE;
524 }
525
526 output_token->length = data.length;
527 output_token->value = data.data;
528
529 if (actual_mech_type)
530 *actual_mech_type = GSS_NTLM_MECHANISM;
531 if (ret_flags)
532 *ret_flags = 0;
533 if (time_rec)
534 *time_rec = GSS_C_INDEFINITE;
535
536 ctx->status |= STATUS_OPEN;
537
538 return GSS_S_COMPLETE;
539 }
540 }
541