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