xref: /freebsd/crypto/heimdal/lib/gssapi/ntlm/init_sec_context.c (revision 70e0bbedef95258a4dadc996d641a9bebd3f107d)
1 /*
2  * Copyright (c) 2006 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "ntlm/ntlm.h"
35 
36 RCSID("$Id: init_sec_context.c 22382 2007-12-30 12:13:17Z lha $");
37 
38 static int
39 from_file(const char *fn, const char *target_domain,
40 	  char **username, struct ntlm_buf *key)
41 {
42     char *str, buf[1024];
43     FILE *f;
44 
45     f = fopen(fn, "r");
46     if (f == NULL)
47 	return ENOENT;
48 
49     while (fgets(buf, sizeof(buf), f) != NULL) {
50 	char *d, *u, *p;
51 	buf[strcspn(buf, "\r\n")] = '\0';
52 	if (buf[0] == '#')
53 	    continue;
54 	str = NULL;
55 	d = strtok_r(buf, ":", &str);
56 	if (d && strcasecmp(target_domain, d) != 0)
57 	    continue;
58 	u = strtok_r(NULL, ":", &str);
59 	p = strtok_r(NULL, ":", &str);
60 	if (u == NULL || p == NULL)
61 	    continue;
62 
63 	*username = strdup(u);
64 
65 	heim_ntlm_nt_key(p, key);
66 
67 	memset(buf, 0, sizeof(buf));
68 	fclose(f);
69 	return 0;
70     }
71     memset(buf, 0, sizeof(buf));
72     fclose(f);
73     return ENOENT;
74 }
75 
76 static int
77 get_user_file(const ntlm_name target_name,
78 	      char **username, struct ntlm_buf *key)
79 {
80     const char *fn;
81 
82     if (issuid())
83 	return ENOENT;
84 
85     fn = getenv("NTLM_USER_FILE");
86     if (fn == NULL)
87 	return ENOENT;
88     if (from_file(fn, target_name->domain, username, key) == 0)
89 	return 0;
90 
91     return ENOENT;
92 }
93 
94 /*
95  * Pick up the ntlm cred from the default krb5 credential cache.
96  */
97 
98 static int
99 get_user_ccache(const ntlm_name name, char **username, struct ntlm_buf *key)
100 {
101     krb5_principal client;
102     krb5_context context = NULL;
103     krb5_error_code ret;
104     krb5_ccache id = NULL;
105     krb5_creds mcreds, creds;
106 
107     *username = NULL;
108     key->length = 0;
109     key->data = NULL;
110 
111     memset(&creds, 0, sizeof(creds));
112     memset(&mcreds, 0, sizeof(mcreds));
113 
114     ret = krb5_init_context(&context);
115     if (ret)
116 	return ret;
117 
118     ret = krb5_cc_default(context, &id);
119     if (ret)
120 	goto out;
121 
122     ret = krb5_cc_get_principal(context, id, &client);
123     if (ret)
124 	goto out;
125 
126     ret = krb5_unparse_name_flags(context, client,
127 				  KRB5_PRINCIPAL_UNPARSE_NO_REALM,
128 				  username);
129     if (ret)
130 	goto out;
131 
132     ret = krb5_make_principal(context, &mcreds.server,
133 			      krb5_principal_get_realm(context, client),
134 			      "@ntlm-key", name->domain, NULL);
135     krb5_free_principal(context, client);
136     if (ret)
137 	goto out;
138 
139     mcreds.session.keytype = ENCTYPE_ARCFOUR_HMAC_MD5;
140     ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_MATCH_KEYTYPE,
141 				&mcreds, &creds);
142     if (ret) {
143 	char *s = krb5_get_error_message(context, ret);
144 	krb5_free_error_string(context, s);
145 	goto out;
146     }
147 
148     key->data = malloc(creds.session.keyvalue.length);
149     if (key->data == NULL)
150 	goto out;
151     key->length = creds.session.keyvalue.length;
152     memcpy(key->data, creds.session.keyvalue.data, key->length);
153 
154     krb5_free_cred_contents(context, &creds);
155 
156     return 0;
157 
158 out:
159     if (*username) {
160 	free(*username);
161 	*username = NULL;
162     }
163     krb5_free_cred_contents(context, &creds);
164     if (mcreds.server)
165 	krb5_free_principal(context, mcreds.server);
166     if (id)
167 	krb5_cc_close(context, id);
168     if (context)
169 	krb5_free_context(context);
170 
171     return ret;
172 }
173 
174 int
175 _gss_ntlm_get_user_cred(const ntlm_name target_name,
176 			ntlm_cred *rcred)
177 {
178     ntlm_cred cred;
179     int ret;
180 
181     cred = calloc(1, sizeof(*cred));
182     if (cred == NULL)
183 	return ENOMEM;
184 
185     ret = get_user_file(target_name, &cred->username, &cred->key);
186     if (ret)
187 	ret = get_user_ccache(target_name, &cred->username, &cred->key);
188     if (ret) {
189 	free(cred);
190 	return ret;
191     }
192 
193     cred->domain = strdup(target_name->domain);
194     *rcred = cred;
195 
196     return ret;
197 }
198 
199 static int
200 _gss_copy_cred(ntlm_cred from, ntlm_cred *to)
201 {
202     *to = calloc(1, sizeof(*to));
203     if (*to == NULL)
204 	return ENOMEM;
205     (*to)->username = strdup(from->username);
206     if ((*to)->username == NULL) {
207 	free(*to);
208 	return ENOMEM;
209     }
210     (*to)->domain = strdup(from->domain);
211     if ((*to)->domain == NULL) {
212 	free((*to)->username);
213 	free(*to);
214 	return ENOMEM;
215     }
216     (*to)->key.data = malloc(from->key.length);
217     if ((*to)->key.data == NULL) {
218 	free((*to)->domain);
219 	free((*to)->username);
220 	free(*to);
221 	return ENOMEM;
222     }
223     memcpy((*to)->key.data, from->key.data, from->key.length);
224     (*to)->key.length = from->key.length;
225 
226     return 0;
227 }
228 
229 OM_uint32
230 _gss_ntlm_init_sec_context
231            (OM_uint32 * minor_status,
232             const gss_cred_id_t initiator_cred_handle,
233             gss_ctx_id_t * context_handle,
234             const gss_name_t target_name,
235             const gss_OID mech_type,
236             OM_uint32 req_flags,
237             OM_uint32 time_req,
238             const gss_channel_bindings_t input_chan_bindings,
239             const gss_buffer_t input_token,
240             gss_OID * actual_mech_type,
241             gss_buffer_t output_token,
242             OM_uint32 * ret_flags,
243             OM_uint32 * time_rec
244 	   )
245 {
246     ntlm_ctx ctx;
247     ntlm_name name = (ntlm_name)target_name;
248 
249     *minor_status = 0;
250 
251     if (ret_flags)
252 	*ret_flags = 0;
253     if (time_rec)
254 	*time_rec = 0;
255     if (actual_mech_type)
256 	*actual_mech_type = GSS_C_NO_OID;
257 
258     if (*context_handle == GSS_C_NO_CONTEXT) {
259 	struct ntlm_type1 type1;
260 	struct ntlm_buf data;
261 	uint32_t flags = 0;
262 	int ret;
263 
264 	ctx = calloc(1, sizeof(*ctx));
265 	if (ctx == NULL) {
266 	    *minor_status = EINVAL;
267 	    return GSS_S_FAILURE;
268 	}
269 	*context_handle = (gss_ctx_id_t)ctx;
270 
271 	if (initiator_cred_handle != GSS_C_NO_CREDENTIAL) {
272 	    ntlm_cred cred = (ntlm_cred)initiator_cred_handle;
273 	    ret = _gss_copy_cred(cred, &ctx->client);
274 	} else
275 	    ret = _gss_ntlm_get_user_cred(name, &ctx->client);
276 
277 	if (ret) {
278 	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
279 	    *minor_status = ret;
280 	    return GSS_S_FAILURE;
281 	}
282 
283 	if (req_flags & GSS_C_CONF_FLAG)
284 	    flags |= NTLM_NEG_SEAL;
285 	if (req_flags & GSS_C_INTEG_FLAG)
286 	    flags |= NTLM_NEG_SIGN;
287 	else
288 	    flags |= NTLM_NEG_ALWAYS_SIGN;
289 
290 	flags |= NTLM_NEG_UNICODE;
291 	flags |= NTLM_NEG_NTLM;
292 	flags |= NTLM_NEG_NTLM2_SESSION;
293 	flags |= NTLM_NEG_KEYEX;
294 
295 	memset(&type1, 0, sizeof(type1));
296 
297 	type1.flags = flags;
298 	type1.domain = name->domain;
299 	type1.hostname = NULL;
300 	type1.os[0] = 0;
301 	type1.os[1] = 0;
302 
303 	ret = heim_ntlm_encode_type1(&type1, &data);
304 	if (ret) {
305 	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
306 	    *minor_status = ret;
307 	    return GSS_S_FAILURE;
308 	}
309 
310 	output_token->value = data.data;
311 	output_token->length = data.length;
312 
313 	return GSS_S_CONTINUE_NEEDED;
314     } else {
315 	krb5_error_code ret;
316 	struct ntlm_type2 type2;
317 	struct ntlm_type3 type3;
318 	struct ntlm_buf data;
319 
320 	ctx = (ntlm_ctx)*context_handle;
321 
322 	data.data = input_token->value;
323 	data.length = input_token->length;
324 
325 	ret = heim_ntlm_decode_type2(&data, &type2);
326 	if (ret) {
327 	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
328 	    *minor_status = ret;
329 	    return GSS_S_FAILURE;
330 	}
331 
332 	ctx->flags = type2.flags;
333 
334 	/* XXX check that type2.targetinfo matches `target_name� */
335 	/* XXX check verify targetinfo buffer */
336 
337 	memset(&type3, 0, sizeof(type3));
338 
339 	type3.username = ctx->client->username;
340 	type3.flags = type2.flags;
341 	type3.targetname = type2.targetname;
342 	type3.ws = rk_UNCONST("workstation");
343 
344 	/*
345 	 * NTLM Version 1 if no targetinfo buffer.
346 	 */
347 
348 	if (1 || type2.targetinfo.length == 0) {
349 	    struct ntlm_buf sessionkey;
350 
351 	    if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
352 		unsigned char nonce[8];
353 
354 		if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
355 		    _gss_ntlm_delete_sec_context(minor_status,
356 						 context_handle, NULL);
357 		    *minor_status = EINVAL;
358 		    return GSS_S_FAILURE;
359 		}
360 
361 		ret = heim_ntlm_calculate_ntlm2_sess(nonce,
362 						     type2.challange,
363 						     ctx->client->key.data,
364 						     &type3.lm,
365 						     &type3.ntlm);
366 	    } else {
367 		ret = heim_ntlm_calculate_ntlm1(ctx->client->key.data,
368 						ctx->client->key.length,
369 						type2.challange,
370 						&type3.ntlm);
371 
372 	    }
373 	    if (ret) {
374 		_gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
375 		*minor_status = ret;
376 		return GSS_S_FAILURE;
377 	    }
378 
379 	    ret = heim_ntlm_build_ntlm1_master(ctx->client->key.data,
380 					       ctx->client->key.length,
381 					       &sessionkey,
382 					       &type3.sessionkey);
383 	    if (ret) {
384 		if (type3.lm.data)
385 		    free(type3.lm.data);
386 		if (type3.ntlm.data)
387 		    free(type3.ntlm.data);
388 		_gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
389 		*minor_status = ret;
390 		return GSS_S_FAILURE;
391 	    }
392 
393 	    ret = krb5_data_copy(&ctx->sessionkey,
394 				 sessionkey.data, sessionkey.length);
395 	    free(sessionkey.data);
396 	    if (ret) {
397 		if (type3.lm.data)
398 		    free(type3.lm.data);
399 		if (type3.ntlm.data)
400 		    free(type3.ntlm.data);
401 		_gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
402 		*minor_status = ret;
403 		return GSS_S_FAILURE;
404 	    }
405 	    ctx->status |= STATUS_SESSIONKEY;
406 
407 	} else {
408 	    struct ntlm_buf sessionkey;
409 	    unsigned char ntlmv2[16];
410 	    struct ntlm_targetinfo ti;
411 
412 	    /* verify infotarget */
413 
414 	    ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
415 	    if(ret) {
416 		_gss_ntlm_delete_sec_context(minor_status,
417 					     context_handle, NULL);
418 		*minor_status = ret;
419 		return GSS_S_FAILURE;
420 	    }
421 
422 	    if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
423 		_gss_ntlm_delete_sec_context(minor_status,
424 					     context_handle, NULL);
425 		*minor_status = EINVAL;
426 		return GSS_S_FAILURE;
427 	    }
428 
429 	    ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
430 					    ctx->client->key.length,
431 					    ctx->client->username,
432 					    name->domain,
433 					    type2.challange,
434 					    &type2.targetinfo,
435 					    ntlmv2,
436 					    &type3.ntlm);
437 	    if (ret) {
438 		_gss_ntlm_delete_sec_context(minor_status,
439 					     context_handle, NULL);
440 		*minor_status = ret;
441 		return GSS_S_FAILURE;
442 	    }
443 
444 	    ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
445 					       &sessionkey,
446 					       &type3.sessionkey);
447 	    memset(ntlmv2, 0, sizeof(ntlmv2));
448 	    if (ret) {
449 		_gss_ntlm_delete_sec_context(minor_status,
450 					     context_handle, NULL);
451 		*minor_status = ret;
452 		return GSS_S_FAILURE;
453 	    }
454 
455 	    ctx->flags |= NTLM_NEG_NTLM2_SESSION;
456 
457 	    ret = krb5_data_copy(&ctx->sessionkey,
458 				 sessionkey.data, sessionkey.length);
459 	    free(sessionkey.data);
460 	}
461 
462 	if (ctx->flags & NTLM_NEG_NTLM2_SESSION) {
463 	    ctx->status |= STATUS_SESSIONKEY;
464 	    _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
465 			      ctx->sessionkey.data,
466 			      ctx->sessionkey.length);
467 	    _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
468 			      ctx->sessionkey.data,
469 			      ctx->sessionkey.length);
470 	} else {
471 	    ctx->status |= STATUS_SESSIONKEY;
472 	    RC4_set_key(&ctx->u.v1.crypto_recv.key,
473 			ctx->sessionkey.length,
474 			ctx->sessionkey.data);
475 	    RC4_set_key(&ctx->u.v1.crypto_send.key,
476 			ctx->sessionkey.length,
477 			ctx->sessionkey.data);
478 	}
479 
480 
481 
482 	ret = heim_ntlm_encode_type3(&type3, &data);
483 	free(type3.sessionkey.data);
484 	if (type3.lm.data)
485 	    free(type3.lm.data);
486 	if (type3.ntlm.data)
487 	    free(type3.ntlm.data);
488 	if (ret) {
489 	    _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
490 	    *minor_status = ret;
491 	    return GSS_S_FAILURE;
492 	}
493 
494 	output_token->length = data.length;
495 	output_token->value = data.data;
496 
497 	if (actual_mech_type)
498 	    *actual_mech_type = GSS_NTLM_MECHANISM;
499 	if (ret_flags)
500 	    *ret_flags = 0;
501 	if (time_rec)
502 	    *time_rec = GSS_C_INDEFINITE;
503 
504 	ctx->status |= STATUS_OPEN;
505 
506 	return GSS_S_COMPLETE;
507     }
508 }
509