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