/src/gss-ntlmssp/src/gss_names.c
Line | Count | Source |
1 | | /* Copyright 2013-2022 Simo Sorce <simo@samba.org>, see COPYING for license */ |
2 | | |
3 | | #define _GNU_SOURCE |
4 | | |
5 | | #include <ctype.h> |
6 | | #include <errno.h> |
7 | | #include <limits.h> |
8 | | #include <pwd.h> |
9 | | #include <stdio.h> |
10 | | #include <stdlib.h> |
11 | | #include <string.h> |
12 | | #include <sys/types.h> |
13 | | #include <unistd.h> |
14 | | |
15 | | #include <gssapi/gssapi.h> |
16 | | #include <gssapi/gssapi_ext.h> |
17 | | |
18 | | #include "gss_ntlmssp.h" |
19 | | |
20 | | #ifndef HOST_NAME_MAX |
21 | | #include <sys/param.h> |
22 | | #define HOST_NAME_MAX MAXHOSTNAMELEN |
23 | | #endif |
24 | | |
25 | | static uint32_t string_split(uint32_t *minor_status, char sep, |
26 | | const char *str, size_t len, |
27 | | char **s1, char **s2) |
28 | 29 | { |
29 | 29 | uint32_t retmaj; |
30 | 29 | uint32_t retmin; |
31 | 29 | char *r1 = NULL; |
32 | 29 | char *r2 = NULL; |
33 | 29 | const char *p; |
34 | 29 | size_t l; |
35 | | |
36 | 29 | p = memchr(str, sep, len); |
37 | 29 | if (!p) return GSSERRS(0, GSS_S_UNAVAILABLE); |
38 | | |
39 | | /* left side */ |
40 | 29 | l = p - str; |
41 | 29 | if (s1 && l != 0) { |
42 | 18 | r1 = strndup(str, l); |
43 | 18 | if (!r1) { |
44 | 0 | set_GSSERR(ENOMEM); |
45 | 0 | goto done; |
46 | 0 | } |
47 | 18 | } |
48 | | |
49 | | /* right side */ |
50 | 29 | p++; |
51 | 29 | l = len - (p - str); |
52 | 29 | if (s2 && l != 0) { |
53 | 29 | r2 = strndup(p, l); |
54 | 29 | if (!r2) { |
55 | 0 | set_GSSERR(ENOMEM); |
56 | 0 | goto done; |
57 | 0 | } |
58 | 29 | } |
59 | | |
60 | 29 | set_GSSERRS(0, GSS_S_COMPLETE); |
61 | | |
62 | 29 | done: |
63 | 29 | if (retmaj) { |
64 | 0 | free(r1); |
65 | 0 | free(r2); |
66 | 29 | } else { |
67 | 29 | if (s1) *s1 = r1; |
68 | 29 | if (s2) *s2 = r2; |
69 | 29 | } |
70 | 29 | return GSSERR(); |
71 | 29 | } |
72 | | |
73 | | /* Form of names allowed in GSSNTLMSSP now: |
74 | | * |
75 | | * Standard Forms: |
76 | | * foo |
77 | | * USERNAME: foo |
78 | | * DOMAIN: <null> |
79 | | * |
80 | | * BAR\foo |
81 | | * USERNAME: foo |
82 | | * DOMAIN: BAR |
83 | | * |
84 | | * foo@BAR |
85 | | * USERNAME: foo |
86 | | * DOMAIN: BAR |
87 | | * |
88 | | * Enterprise name forms: |
89 | | * foo\@bar.example.com |
90 | | * USERNAME: foo@bar.example.com |
91 | | * DOMAIN: <null> |
92 | | * |
93 | | * foo\@bar.example.com@BAR |
94 | | * USERNAME: foo@bar.example.com |
95 | | * DOMAIN: BAR |
96 | | * |
97 | | * \foo@bar.example.com |
98 | | * USERNAME: foo@bar.example.com |
99 | | * DOMAIN: <null> |
100 | | * |
101 | | * BAR\foo@bar.example.com |
102 | | * USERNAME: foo@bar.example.com |
103 | | * DOMAIN: BAR |
104 | | * |
105 | | * BAR@dom\foo@bar.example.com |
106 | | * USERNAME: foo@bar.example.com |
107 | | * DOMAIN: BAR@dom |
108 | | * |
109 | | * Invalid forms: |
110 | | * BAR@dom\@foo.. |
111 | | * DOM\foo\@bar |
112 | | * foo@bar\@baz |
113 | | */ |
114 | 106 | #define MAX_NAME_LEN 1024 |
115 | | static uint32_t parse_user_name(uint32_t *minor_status, |
116 | | const char *str, size_t len, |
117 | | char **domain, char **username) |
118 | 106 | { |
119 | 106 | uint32_t retmaj; |
120 | 106 | uint32_t retmin; |
121 | 106 | char *at, *sep; |
122 | | |
123 | 106 | if (len > MAX_NAME_LEN) { |
124 | 0 | return GSSERRS(ERR_NAMETOOLONG, GSS_S_BAD_NAME); |
125 | 0 | } |
126 | | |
127 | 106 | *username = NULL; |
128 | 106 | *domain = NULL; |
129 | | |
130 | | /* let's check if there are '@' or '\' signs */ |
131 | 106 | at = memchr(str, '@', len); |
132 | 106 | sep = memchr(str, '\\', len); |
133 | | |
134 | | /* Check if enterprise name first */ |
135 | 106 | if (at && sep) { |
136 | | /* we may have an enterprise name here */ |
137 | 77 | char strbuf[len + 1]; |
138 | 77 | char *buf = strbuf; |
139 | | |
140 | | /* copy buf to manipulate it */ |
141 | 77 | memcpy(buf, str, len); |
142 | 77 | buf[len] = '\0'; |
143 | | |
144 | | /* adjust pointers relative to new buffer */ |
145 | 77 | sep = buf + (sep - str); |
146 | 77 | at = buf + (at - str); |
147 | | |
148 | 77 | if (sep > at) { |
149 | | /* domain name contains an '@' sign ... */ |
150 | 31 | if (*(sep + 1) == '@') { |
151 | | /* invalid case of XXX@YYY\@ZZZ*/ |
152 | 4 | set_GSSERR(EINVAL); |
153 | 4 | goto done; |
154 | 4 | } |
155 | 46 | } else if (at - sep == 1) { |
156 | | /* it's just a '\@' escape */ |
157 | | /* no leading domain */ |
158 | 17 | sep = NULL; |
159 | 17 | } |
160 | | |
161 | 73 | if (sep) { |
162 | | /* terminate and copy domain, even if empty */ |
163 | | /* NOTE: this is important for the Windbind integration case |
164 | | * where we need to tell the machinery to *not* add the default |
165 | | * domain name, it happens when the domain is NULL. */ |
166 | 56 | *sep = '\0'; |
167 | 56 | *domain = strdup(buf); |
168 | 56 | if (NULL == *domain) { |
169 | 0 | set_GSSERR(ENOMEM); |
170 | 0 | goto done; |
171 | 0 | } |
172 | | /* point buf at username part */ |
173 | 56 | len = len - (sep - buf) - 1; |
174 | 56 | buf = sep + 1; |
175 | 56 | } |
176 | | |
177 | 1.28k | for (at = strchr(buf, '@'); at != NULL; at = strchr(at, '@')) { |
178 | 1.22k | if (*(at - 1) == '\\') { |
179 | 225 | if (*domain) { |
180 | | /* Invalid forms like DOM\foo\@bar or foo@bar\@baz */ |
181 | 8 | free(*domain); |
182 | 8 | *domain = NULL; |
183 | 8 | set_GSSERR(EINVAL); |
184 | 8 | goto done; |
185 | 8 | } |
186 | | /* remove escape, moving all including terminating '\0' */ |
187 | 217 | memmove(at - 1, at, len - (at - buf) + 1); |
188 | 998 | } else if (!*domain) { |
189 | | /* an '@' without escape and no previous |
190 | | * domain was split out. |
191 | | * the rest of the string is the domain */ |
192 | 7 | *at = '\0'; |
193 | 7 | *domain = strdup(at + 1); |
194 | 7 | if (NULL == *domain) { |
195 | 0 | set_GSSERR(ENOMEM); |
196 | 0 | goto done; |
197 | 0 | } |
198 | | /* note we continue the loop to check if any invalid |
199 | | * \@ escapes is found in the domain part */ |
200 | 7 | } |
201 | 1.21k | at += 1; |
202 | 1.21k | } |
203 | | |
204 | 65 | *username = strdup(buf); |
205 | 65 | if (NULL == *username) { |
206 | 0 | set_GSSERR(ENOMEM); |
207 | 0 | goto done; |
208 | 0 | } |
209 | | |
210 | | /* we got an enterprise name, return */ |
211 | 65 | set_GSSERRS(0, GSS_S_COMPLETE); |
212 | 65 | goto done; |
213 | 65 | } |
214 | | |
215 | | /* Check if in classic DOMAIN\User windows format */ |
216 | 29 | if (sep) { |
217 | 29 | retmaj = string_split(&retmin, '\\', str, len, domain, username); |
218 | 29 | goto done; |
219 | 29 | } |
220 | | |
221 | | /* else accept a user@domain format too */ |
222 | 0 | if (at) { |
223 | 0 | retmaj = string_split(&retmin, '@', str, len, username, domain); |
224 | 0 | goto done; |
225 | 0 | } |
226 | | |
227 | | /* finally, take string as simple user name */ |
228 | 0 | *username = strndup(str, len); |
229 | 0 | if (NULL == *username) { |
230 | 0 | set_GSSERR(ENOMEM); |
231 | 0 | } |
232 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
233 | |
|
234 | 106 | done: |
235 | 106 | return GSSERR(); |
236 | 0 | } |
237 | | |
238 | | static uint32_t uid_to_name(uint32_t *minor_status, uid_t uid, char **name) |
239 | 0 | { |
240 | 0 | uint32_t retmaj; |
241 | 0 | uint32_t retmin; |
242 | 0 | struct passwd *pw; |
243 | |
|
244 | 0 | pw = getpwuid(uid); |
245 | 0 | if (pw) { |
246 | 0 | return GSSERRS(ERR_NOUSRFOUND, GSS_S_FAILURE); |
247 | 0 | } |
248 | 0 | *name = strdup(pw->pw_name); |
249 | 0 | if (!*name) { |
250 | 0 | set_GSSERR(ENOMEM); |
251 | 0 | goto done; |
252 | 0 | } |
253 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
254 | |
|
255 | 0 | done: |
256 | 0 | return GSSERR(); |
257 | 0 | } |
258 | | |
259 | | uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status, |
260 | | gss_const_OID mech_type, |
261 | | gss_buffer_t input_name_buffer, |
262 | | gss_OID input_name_type, |
263 | | gss_name_t *output_name) |
264 | 1.29k | { |
265 | 1.29k | struct gssntlm_name *name = NULL; |
266 | 1.29k | uint32_t retmaj; |
267 | 1.29k | uint32_t retmin; |
268 | | |
269 | | /* TODO: check mech_type == gssntlm_oid */ |
270 | 1.29k | if (mech_type == GSS_C_NO_OID) { |
271 | 0 | return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); |
272 | 0 | } |
273 | | |
274 | 1.29k | name = calloc(1, sizeof(struct gssntlm_name)); |
275 | 1.29k | if (!name) { |
276 | 0 | set_GSSERR(ENOMEM); |
277 | 0 | goto done; |
278 | 0 | } |
279 | | |
280 | | /* treat null OID like NT_USER_NAME */ |
281 | 1.29k | if (input_name_type == GSS_C_NULL_OID) { |
282 | 0 | input_name_type = GSS_C_NT_USER_NAME; |
283 | 0 | } |
284 | | |
285 | 1.29k | if (gss_oid_equal(input_name_type, GSS_C_NT_HOSTBASED_SERVICE) || |
286 | 1.18k | gss_oid_equal(input_name_type, GSS_C_NT_HOSTBASED_SERVICE_X)) { |
287 | 1.18k | char *spn = NULL; |
288 | 1.18k | char *p = NULL; |
289 | | |
290 | 1.18k | name->type = GSSNTLM_NAME_SERVER; |
291 | | |
292 | 1.18k | if (input_name_buffer->length > 0) { |
293 | 0 | spn = strndup(input_name_buffer->value, input_name_buffer->length); |
294 | 0 | if (!spn) { |
295 | 0 | set_GSSERR(ENOMEM); |
296 | 0 | goto done; |
297 | 0 | } |
298 | 0 | p = strchr(spn, '@'); |
299 | 0 | if (p && input_name_buffer->length == 1) { |
300 | 0 | free(spn); |
301 | 0 | spn = p = NULL; |
302 | 0 | } |
303 | 0 | } |
304 | | |
305 | 1.18k | if (p) { |
306 | | /* Windows expects a SPN not a GSS Name */ |
307 | 0 | if (p != spn) { |
308 | 0 | *p = '/'; |
309 | 0 | name->data.server.spn = spn; |
310 | 0 | spn = NULL; |
311 | 0 | } |
312 | 0 | p += 1; |
313 | 0 | name->data.server.name = strdup(p); |
314 | 0 | if (!name->data.server.name) { |
315 | 0 | free(spn); |
316 | 0 | set_GSSERR(ENOMEM); |
317 | 0 | goto done; |
318 | 0 | } |
319 | 1.18k | } else { |
320 | 1.18k | char hostname[HOST_NAME_MAX + 1] = { 0 }; |
321 | 1.18k | size_t l, r; |
322 | | /* no seprator, assume only service is provided and try to |
323 | | * source the local host name */ |
324 | 1.18k | retmin = gethostname(hostname, HOST_NAME_MAX); |
325 | 1.18k | if (retmin) { |
326 | 0 | free(spn); |
327 | 0 | set_GSSERR(retmin); |
328 | 0 | goto done; |
329 | 0 | } |
330 | 1.18k | hostname[HOST_NAME_MAX] = '\0'; |
331 | 1.18k | if (spn != NULL) { |
332 | | /* spn = <service> + </> + <hostname> + <\0> */ |
333 | 0 | l = strlen(spn) + 1 + strlen(hostname) + 1; |
334 | 0 | name->data.server.spn = malloc(l); |
335 | 0 | if (!name->data.server.spn) { |
336 | 0 | free(spn); |
337 | 0 | set_GSSERR(ENOMEM); |
338 | 0 | goto done; |
339 | 0 | } |
340 | 0 | r = snprintf(name->data.server.spn, l, "%s/%s", spn, hostname); |
341 | 0 | if (r != l - 1) { |
342 | 0 | free(spn); |
343 | 0 | set_GSSERR(ENOMEM); |
344 | 0 | goto done; |
345 | 0 | } |
346 | 0 | } |
347 | 1.18k | name->data.server.name = strdup(hostname); |
348 | 1.18k | if (!name->data.server.name) { |
349 | 0 | free(spn); |
350 | 0 | set_GSSERR(ENOMEM); |
351 | 0 | goto done; |
352 | 0 | } |
353 | 1.18k | } |
354 | 1.18k | free(spn); |
355 | 1.18k | set_GSSERRS(0, GSS_S_COMPLETE); |
356 | | |
357 | 1.18k | } else if (gss_oid_equal(input_name_type, GSS_C_NT_USER_NAME)) { |
358 | | |
359 | 106 | name->type = GSSNTLM_NAME_USER; |
360 | 106 | retmaj = parse_user_name(&retmin, |
361 | 106 | input_name_buffer->value, |
362 | 106 | input_name_buffer->length, |
363 | 106 | &name->data.user.domain, |
364 | 106 | &name->data.user.name); |
365 | 106 | } else if (gss_oid_equal(input_name_type, GSS_C_NT_MACHINE_UID_NAME)) { |
366 | 0 | uid_t uid; |
367 | |
|
368 | 0 | name->type = GSSNTLM_NAME_USER; |
369 | 0 | name->data.user.domain = NULL; |
370 | |
|
371 | 0 | uid = *(uid_t *)input_name_buffer->value; |
372 | 0 | retmaj = uid_to_name(&retmin, uid, &name->data.user.name); |
373 | 0 | } else if (gss_oid_equal(input_name_type, GSS_C_NT_STRING_UID_NAME)) { |
374 | 0 | char struid[12] = { 0 }; |
375 | 0 | uid_t uid; |
376 | |
|
377 | 0 | name->type = GSSNTLM_NAME_USER; |
378 | 0 | name->data.user.domain = NULL; |
379 | |
|
380 | 0 | if (input_name_buffer->length > 12) { |
381 | 0 | set_GSSERR(ERR_BADARG); |
382 | 0 | goto done; |
383 | 0 | } |
384 | 0 | memcpy(struid, input_name_buffer->value, input_name_buffer->length); |
385 | 0 | struid[11] = '\0'; |
386 | 0 | errno = 0; |
387 | 0 | uid = strtol(struid, NULL, 10); |
388 | 0 | if (errno) { |
389 | 0 | set_GSSERR(ERR_BADARG); |
390 | 0 | goto done; |
391 | 0 | } |
392 | 0 | retmaj = uid_to_name(&retmin, uid, &name->data.user.name); |
393 | 0 | } else if (gss_oid_equal(input_name_type, GSS_C_NT_ANONYMOUS)) { |
394 | 0 | name->type = GSSNTLM_NAME_ANON; |
395 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
396 | 0 | } else if (gss_oid_equal(input_name_type, GSS_C_NT_EXPORT_NAME)) { |
397 | | /* TODO */ |
398 | 0 | set_GSSERRS(ERR_NOTSUPPORTED, GSS_S_BAD_NAMETYPE); |
399 | 0 | } else { |
400 | 0 | set_GSSERRS(ERR_BADARG, GSS_S_BAD_NAMETYPE); |
401 | 0 | } |
402 | | |
403 | 1.29k | done: |
404 | 1.29k | if (retmaj != GSS_S_COMPLETE) { |
405 | 12 | uint32_t tmpmin; |
406 | 12 | gssntlm_release_name(&tmpmin, (gss_name_t *)&name); |
407 | 1.28k | } else { |
408 | 1.28k | *output_name = (gss_name_t)name; |
409 | 1.28k | } |
410 | 1.29k | return GSSERR(); |
411 | 1.29k | } |
412 | | |
413 | | uint32_t gssntlm_import_name(uint32_t *minor_status, |
414 | | gss_buffer_t input_name_buffer, |
415 | | gss_OID input_name_type, |
416 | | gss_name_t *output_name) |
417 | 106 | { |
418 | 106 | return gssntlm_import_name_by_mech(minor_status, |
419 | 106 | discard_const(&gssntlm_oid), |
420 | 106 | input_name_buffer, |
421 | 106 | input_name_type, |
422 | 106 | output_name); |
423 | 106 | } |
424 | | |
425 | | size_t gssntlm_get_attrs_count(const struct gssntlm_name_attribute *attrs) |
426 | 1.18k | { |
427 | 1.18k | size_t c; |
428 | 1.18k | for (c = 0; attrs && attrs[c].attr_name != NULL; c++) ; |
429 | 1.18k | return c; |
430 | 1.18k | } |
431 | | |
432 | | int gssntlm_copy_attrs(const struct gssntlm_name_attribute *src, |
433 | | struct gssntlm_name_attribute **dst) |
434 | 1.18k | { |
435 | 1.18k | struct gssntlm_name_attribute *copied_attrs; |
436 | 1.18k | size_t attrs_count = gssntlm_get_attrs_count(src); |
437 | | |
438 | 1.18k | *dst = NULL; |
439 | 1.18k | if (attrs_count == 0) { |
440 | 1.18k | return 0; |
441 | 1.18k | } |
442 | | |
443 | 0 | copied_attrs = calloc(attrs_count + 1, /* +1 for terminator entry */ |
444 | 0 | sizeof(struct gssntlm_name_attribute)); |
445 | 0 | if (copied_attrs == NULL) { |
446 | 0 | return ENOMEM; |
447 | 0 | } |
448 | | |
449 | 0 | for (size_t i = 0; i < attrs_count; i++) { |
450 | 0 | copied_attrs[i].attr_name = strdup(src[i].attr_name); |
451 | 0 | if (copied_attrs[i].attr_name == NULL) { |
452 | 0 | gssntlm_release_attrs(&copied_attrs); |
453 | 0 | return ENOMEM; |
454 | 0 | } |
455 | 0 | copied_attrs[i].attr_value.length = src[i].attr_value.length; |
456 | 0 | copied_attrs[i].attr_value.value = malloc(src[i].attr_value.length); |
457 | 0 | if (copied_attrs[i].attr_value.value == NULL) { |
458 | 0 | gssntlm_release_attrs(&copied_attrs); |
459 | 0 | return ENOMEM; |
460 | 0 | } |
461 | 0 | memcpy(copied_attrs[i].attr_value.value, src[i].attr_value.value, |
462 | 0 | src[i].attr_value.length); |
463 | 0 | } |
464 | | /* terminator entry is filled with zeroes by calloc */ |
465 | | |
466 | 0 | *dst = copied_attrs; |
467 | 0 | return 0; |
468 | 0 | } |
469 | | |
470 | | struct gssntlm_name_attribute *gssntlm_find_attr( |
471 | | struct gssntlm_name_attribute *attrs, |
472 | | const char *attr_name, |
473 | | size_t attr_name_len) |
474 | 0 | { |
475 | 0 | for (size_t i = 0; attrs && (attrs[i].attr_name != NULL); i++) { |
476 | | /* We store attr_name as a zero-terminated string, so |
477 | | * it is always zero-terminated */ |
478 | 0 | if (attr_name_len == strlen(attrs[i].attr_name) && |
479 | 0 | strncasecmp(attrs[i].attr_name, attr_name, attr_name_len) == 0) { |
480 | 0 | return &attrs[i]; |
481 | 0 | } |
482 | 0 | } |
483 | 0 | return NULL; |
484 | 0 | } |
485 | | |
486 | | void gssntlm_release_attrs(struct gssntlm_name_attribute **attrs) |
487 | 2.48k | { |
488 | 2.48k | for (size_t i = 0; *attrs && (*attrs)[i].attr_name != NULL; i++) { |
489 | 0 | free((*attrs)[i].attr_name); |
490 | 0 | free((*attrs)[i].attr_value.value); |
491 | 0 | } |
492 | 2.48k | safefree(*attrs); |
493 | 2.48k | } |
494 | | |
495 | | int gssntlm_copy_name(struct gssntlm_name *src, struct gssntlm_name *dst) |
496 | 1.18k | { |
497 | 1.18k | char *dom = NULL, *usr = NULL, *spn = NULL, *srv = NULL; |
498 | 1.18k | int ret; |
499 | 1.18k | dst->type = src->type; |
500 | 1.18k | switch (src->type) { |
501 | 0 | case GSSNTLM_NAME_NULL: |
502 | 0 | case GSSNTLM_NAME_ANON: |
503 | 0 | break; |
504 | 0 | case GSSNTLM_NAME_USER: |
505 | 0 | if (src->data.user.domain) { |
506 | 0 | dom = strdup(src->data.user.domain); |
507 | 0 | if (!dom) { |
508 | 0 | ret = ENOMEM; |
509 | 0 | goto done; |
510 | 0 | } |
511 | 0 | } |
512 | 0 | if (src->data.user.name) { |
513 | 0 | usr = strdup(src->data.user.name); |
514 | 0 | if (!usr) { |
515 | 0 | ret = ENOMEM; |
516 | 0 | goto done; |
517 | 0 | } |
518 | 0 | } |
519 | 0 | dst->data.user.domain = dom; |
520 | 0 | dst->data.user.name = usr; |
521 | 0 | break; |
522 | 1.18k | case GSSNTLM_NAME_SERVER: |
523 | 1.18k | if (src->data.server.spn) { |
524 | 0 | spn = strdup(src->data.server.spn); |
525 | 0 | if (!spn) { |
526 | 0 | ret = ENOMEM; |
527 | 0 | goto done; |
528 | 0 | } |
529 | 0 | } |
530 | 1.18k | dst->data.server.spn = spn; |
531 | 1.18k | if (src->data.server.name) { |
532 | 1.18k | srv = strdup(src->data.server.name); |
533 | 1.18k | if (!srv) { |
534 | 0 | ret = ENOMEM; |
535 | 0 | goto done; |
536 | 0 | } |
537 | 1.18k | } |
538 | 1.18k | dst->data.server.name = srv; |
539 | 1.18k | break; |
540 | 1.18k | } |
541 | | |
542 | 1.18k | ret = gssntlm_copy_attrs(src->attrs, &dst->attrs); |
543 | 1.18k | if (ret) goto done; |
544 | | |
545 | 1.18k | ret = 0; |
546 | 1.18k | done: |
547 | 1.18k | if (ret) { |
548 | 0 | safefree(dom); |
549 | 0 | safefree(usr); |
550 | 0 | safefree(spn); |
551 | 0 | safefree(srv); |
552 | 0 | } |
553 | 1.18k | return ret; |
554 | 1.18k | } |
555 | | |
556 | | uint32_t gssntlm_duplicate_name(uint32_t *minor_status, |
557 | | const gss_name_t input_name, |
558 | | gss_name_t *dest_name) |
559 | 0 | { |
560 | 0 | struct gssntlm_name *in; |
561 | 0 | struct gssntlm_name *out; |
562 | 0 | uint32_t retmin; |
563 | 0 | uint32_t retmaj; |
564 | |
|
565 | 0 | if (input_name == GSS_C_NO_NAME || dest_name == NULL) { |
566 | 0 | return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); |
567 | 0 | } |
568 | | |
569 | 0 | in = (struct gssntlm_name *)input_name; |
570 | |
|
571 | 0 | if (in->type == GSSNTLM_NAME_NULL) { |
572 | 0 | *dest_name = GSS_C_NO_NAME; |
573 | 0 | return GSSERRS(0, GSS_S_COMPLETE); |
574 | 0 | } |
575 | | |
576 | 0 | out = calloc(1, sizeof(struct gssntlm_name)); |
577 | 0 | if (!out) { |
578 | 0 | set_GSSERR(ENOMEM); |
579 | 0 | goto done; |
580 | 0 | } |
581 | | |
582 | 0 | retmin = gssntlm_copy_name(in, out); |
583 | 0 | if (retmin) { |
584 | 0 | set_GSSERR(retmin); |
585 | 0 | goto done; |
586 | 0 | } |
587 | | |
588 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
589 | |
|
590 | 0 | done: |
591 | 0 | if (retmaj) { |
592 | 0 | safefree(out); |
593 | 0 | } |
594 | 0 | *dest_name = (gss_name_t)out; |
595 | 0 | return GSSERR(); |
596 | 0 | } |
597 | | |
598 | | void gssntlm_int_release_name(struct gssntlm_name *name) |
599 | 7.96k | { |
600 | 7.96k | if (!name) return; |
601 | | |
602 | 3.67k | switch (name->type) { |
603 | 1.18k | case GSSNTLM_NAME_NULL: |
604 | 1.18k | return; |
605 | 0 | case GSSNTLM_NAME_ANON: |
606 | 0 | break; |
607 | 106 | case GSSNTLM_NAME_USER: |
608 | 106 | safefree(name->data.user.domain); |
609 | 106 | safefree(name->data.user.name); |
610 | 106 | break; |
611 | 2.37k | case GSSNTLM_NAME_SERVER: |
612 | 2.37k | safefree(name->data.server.spn); |
613 | 2.37k | safefree(name->data.server.name); |
614 | 2.37k | break; |
615 | 3.67k | } |
616 | 2.48k | gssntlm_release_attrs(&name->attrs); |
617 | 2.48k | name->type = GSSNTLM_NAME_NULL; |
618 | 2.48k | } |
619 | | |
620 | | uint32_t gssntlm_release_name(uint32_t *minor_status, |
621 | | gss_name_t *input_name) |
622 | 5.58k | { |
623 | 5.58k | uint32_t retmaj; |
624 | 5.58k | uint32_t retmin; |
625 | | |
626 | 5.58k | if (!input_name) { |
627 | 0 | return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); |
628 | 0 | } |
629 | | |
630 | 5.58k | gssntlm_int_release_name((struct gssntlm_name *)*input_name); |
631 | | |
632 | 5.58k | safefree(*input_name); |
633 | 5.58k | return GSSERRS(0, GSS_S_COMPLETE); |
634 | 5.58k | } |
635 | | |
636 | | uint32_t gssntlm_display_name(uint32_t *minor_status, |
637 | | gss_name_t input_name, |
638 | | gss_buffer_t output_name_buffer, |
639 | | gss_OID *output_name_type) |
640 | 0 | { |
641 | 0 | struct gssntlm_name *in; |
642 | 0 | gss_buffer_t out; |
643 | 0 | uint32_t retmaj; |
644 | 0 | uint32_t retmin; |
645 | 0 | int ret; |
646 | |
|
647 | 0 | if (input_name == GSS_C_NO_NAME || output_name_buffer == NULL) { |
648 | 0 | return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); |
649 | 0 | } |
650 | | |
651 | 0 | in = (struct gssntlm_name *)input_name; |
652 | 0 | out = output_name_buffer; |
653 | |
|
654 | 0 | switch (in->type) { |
655 | 0 | case GSSNTLM_NAME_NULL: |
656 | 0 | return GSSERRS(ERR_BADARG, GSS_S_BAD_NAME); |
657 | 0 | case GSSNTLM_NAME_ANON: |
658 | 0 | out->value = strdup("NT AUTHORITY\\ANONYMOUS LOGON"); |
659 | 0 | if (!out->value) { |
660 | 0 | set_GSSERR(ENOMEM); |
661 | 0 | goto done; |
662 | 0 | } |
663 | 0 | out->length = strlen(out->value) + 1; |
664 | 0 | if (output_name_type) { |
665 | 0 | *output_name_type = GSS_C_NT_ANONYMOUS; |
666 | 0 | } |
667 | 0 | break; |
668 | 0 | case GSSNTLM_NAME_USER: |
669 | 0 | if (in->data.user.domain) { |
670 | 0 | ret = asprintf((char **)&out->value, "%s\\%s", |
671 | 0 | in->data.user.domain, in->data.user.name); |
672 | 0 | if (ret == -1) { |
673 | 0 | out->value = NULL; |
674 | 0 | } |
675 | 0 | } else { |
676 | 0 | out->value = strdup(in->data.user.name); |
677 | 0 | } |
678 | 0 | if (!out->value) { |
679 | 0 | set_GSSERR(ENOMEM); |
680 | 0 | goto done; |
681 | 0 | } |
682 | 0 | out->length = strlen(out->value) + 1; |
683 | 0 | if (output_name_type) { |
684 | 0 | *output_name_type = GSS_C_NT_USER_NAME; |
685 | 0 | } |
686 | 0 | break; |
687 | 0 | case GSSNTLM_NAME_SERVER: |
688 | 0 | out->value = strdup(in->data.server.spn); |
689 | 0 | if (!out->value) { |
690 | 0 | set_GSSERR(ENOMEM); |
691 | 0 | goto done; |
692 | 0 | } |
693 | 0 | out->length = strlen(out->value) + 1; |
694 | 0 | if (output_name_type) { |
695 | 0 | *output_name_type = GSS_C_NT_HOSTBASED_SERVICE; |
696 | 0 | } |
697 | 0 | break; |
698 | 0 | } |
699 | | |
700 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
701 | |
|
702 | 0 | done: |
703 | 0 | return GSSERR(); |
704 | 0 | } |
705 | | |
706 | 0 | #define PWBUFLEN 1024 |
707 | | |
708 | | uint32_t gssntlm_localname(uint32_t *minor_status, |
709 | | const gss_name_t name, |
710 | | gss_const_OID mech_type, |
711 | | gss_buffer_t localname) |
712 | 0 | { |
713 | 0 | struct gssntlm_name *in; |
714 | 0 | char *uname = NULL; |
715 | 0 | char pwbuf[PWBUFLEN]; |
716 | 0 | struct passwd pw, *res; |
717 | 0 | uint32_t retmaj; |
718 | 0 | uint32_t retmin; |
719 | 0 | int ret; |
720 | |
|
721 | 0 | in = (struct gssntlm_name *)name; |
722 | 0 | if (in->type != GSSNTLM_NAME_USER) { |
723 | 0 | set_GSSERRS(ERR_BADARG, GSS_S_BAD_NAME); |
724 | 0 | goto done; |
725 | 0 | } |
726 | | |
727 | | /* TODO: hook up with winbindd/sssd for name resolution ? */ |
728 | | |
729 | 0 | if (in->data.user.domain) { |
730 | 0 | ret = asprintf(&uname, "%s\\%s", |
731 | 0 | in->data.user.domain, in->data.user.name); |
732 | 0 | if (ret == -1) { |
733 | 0 | set_GSSERR(ENOMEM); |
734 | 0 | goto done; |
735 | 0 | } |
736 | 0 | ret = getpwnam_r(uname, &pw, pwbuf, PWBUFLEN, &res); |
737 | 0 | if (ret) { |
738 | 0 | set_GSSERR(ret); |
739 | 0 | goto done; |
740 | 0 | } |
741 | 0 | safefree(uname); |
742 | 0 | if (res) { |
743 | 0 | uname = strdup(res->pw_name); |
744 | 0 | } |
745 | 0 | } |
746 | 0 | if (uname == NULL) { |
747 | 0 | ret = getpwnam_r(in->data.user.name, &pw, pwbuf, PWBUFLEN, &res); |
748 | 0 | if (ret != 0 || res == NULL) { |
749 | 0 | set_GSSERR(ret); |
750 | 0 | goto done; |
751 | 0 | } |
752 | 0 | uname = strdup(res->pw_name); |
753 | 0 | } |
754 | 0 | if (!uname) { |
755 | 0 | set_GSSERR(ENOMEM); |
756 | 0 | goto done; |
757 | 0 | } |
758 | | |
759 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
760 | |
|
761 | 0 | done: |
762 | 0 | if (retmaj) { |
763 | 0 | safefree(uname); |
764 | 0 | } else { |
765 | 0 | localname->value = uname; |
766 | 0 | localname->length = strlen(uname) + 1; |
767 | 0 | } |
768 | 0 | return GSSERR(); |
769 | 0 | } |
770 | | |
771 | | uint32_t netbios_get_names(void *ctx, char *computer_name, |
772 | | char **netbios_host, char **netbios_domain) |
773 | 1.18k | { |
774 | 1.18k | char *nb_computer_name = NULL; |
775 | 1.18k | char *nb_domain_name = NULL; |
776 | 1.18k | char *env_name; |
777 | 1.18k | uint32_t ret; |
778 | | |
779 | 1.18k | env_name = getenv("NETBIOS_COMPUTER_NAME"); |
780 | 1.18k | if (env_name) { |
781 | 0 | nb_computer_name = strdup(env_name); |
782 | 0 | if (!nb_computer_name) { |
783 | 0 | ret = ENOMEM; |
784 | 0 | goto done; |
785 | 0 | } |
786 | 0 | } |
787 | | |
788 | 1.18k | env_name = getenv("NETBIOS_DOMAIN_NAME"); |
789 | 1.18k | if (env_name) { |
790 | 0 | nb_domain_name = strdup(env_name); |
791 | 0 | if (!nb_domain_name) { |
792 | 0 | ret = ENOMEM; |
793 | 0 | goto done; |
794 | 0 | } |
795 | 0 | } |
796 | | |
797 | 1.18k | if (!nb_computer_name || !nb_domain_name) { |
798 | | /* fetch only mising ones */ |
799 | 1.18k | ret = external_netbios_get_names(ctx, |
800 | 1.18k | nb_computer_name ? NULL : &nb_computer_name, |
801 | 1.18k | nb_domain_name ? NULL : &nb_domain_name); |
802 | 1.18k | if ((ret != 0) && |
803 | 1.18k | (ret != ENOENT) && |
804 | 1.18k | (ret != ERR_NOTAVAIL)) { |
805 | 0 | goto done; |
806 | 0 | } |
807 | 1.18k | } |
808 | | |
809 | 1.18k | if (!nb_computer_name) { |
810 | 1.18k | char *p; |
811 | 1.18k | p = strchr(computer_name, '.'); |
812 | 1.18k | if (p) { |
813 | 0 | nb_computer_name = strndup(computer_name, p - computer_name); |
814 | 1.18k | } else { |
815 | 1.18k | nb_computer_name = strdup(computer_name); |
816 | 1.18k | } |
817 | 15.4k | for (p = nb_computer_name; p && *p; p++) { |
818 | | /* Can only be ASCII, so toupper is safe */ |
819 | 14.2k | *p = toupper(*p); |
820 | 14.2k | } |
821 | 1.18k | if (!nb_computer_name) { |
822 | 0 | ret = ENOMEM; |
823 | 0 | goto done; |
824 | 0 | } |
825 | 1.18k | } |
826 | | |
827 | 1.18k | if (!nb_domain_name) { |
828 | 1.18k | nb_domain_name = strdup(DEF_NB_DOMAIN); |
829 | 1.18k | if (!nb_domain_name) { |
830 | 0 | ret = ENOMEM; |
831 | 0 | goto done; |
832 | 0 | } |
833 | 1.18k | } |
834 | | |
835 | 1.18k | ret = 0; |
836 | | |
837 | 1.18k | done: |
838 | 1.18k | if (ret) { |
839 | 0 | safefree(nb_computer_name); |
840 | 0 | safefree(nb_domain_name); |
841 | 0 | } |
842 | | |
843 | 1.18k | *netbios_domain = nb_domain_name; |
844 | 1.18k | *netbios_host = nb_computer_name; |
845 | 1.18k | return ret; |
846 | 1.18k | } |
847 | | |
848 | | uint32_t gssntlm_inquire_name(uint32_t *minor_status, |
849 | | gss_name_t name, |
850 | | int *name_is_MN, |
851 | | gss_OID *MN_mech, |
852 | | gss_buffer_set_t *attrs) |
853 | 0 | { |
854 | 0 | uint32_t retmin = 0; |
855 | 0 | uint32_t retmaj = 0; |
856 | 0 | uint32_t tmpmin; |
857 | 0 | const struct gssntlm_name *in = (const struct gssntlm_name *)name; |
858 | |
|
859 | 0 | if (!attrs) { |
860 | 0 | return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE); |
861 | 0 | } |
862 | 0 | *attrs = GSS_C_NO_BUFFER_SET; |
863 | |
|
864 | 0 | if (name == GSS_C_NO_NAME) { |
865 | 0 | return GSSERRS(GSS_S_BAD_NAME, GSS_S_CALL_INACCESSIBLE_READ); |
866 | 0 | } |
867 | | |
868 | 0 | for (size_t i = 0; in->attrs && in->attrs[i].attr_name != NULL; i++) { |
869 | 0 | struct gssntlm_name_attribute *attr = &in->attrs[i]; |
870 | 0 | size_t attr_name_len = strlen(attr->attr_name); |
871 | 0 | gss_buffer_desc buf; |
872 | 0 | gss_buffer_t attr_value = &attr->attr_value; |
873 | | /* +1 for '=' separator and +1 for EOL */ |
874 | 0 | size_t full_string_len = attr_value->length + attr_name_len + 2; |
875 | 0 | size_t offset = 0; |
876 | 0 | char *attr_string = malloc(full_string_len); |
877 | 0 | if (attr_string == NULL) { |
878 | 0 | set_GSSERR(ENOMEM); |
879 | 0 | goto done; |
880 | 0 | } |
881 | | |
882 | | /* Construct 'attr_name=<attr_value>\0' string */ |
883 | 0 | memcpy(attr_string, attr->attr_name, attr_name_len); |
884 | 0 | offset += attr_name_len; |
885 | |
|
886 | 0 | attr_string[offset++] = '='; |
887 | |
|
888 | 0 | memcpy(attr_string + offset, attr_value->value, attr_value->length); |
889 | 0 | offset += attr_value->length; |
890 | |
|
891 | 0 | attr_string[offset] = 0; |
892 | | |
893 | | /* now add a buffer to output set */ |
894 | 0 | buf.length = full_string_len; |
895 | 0 | buf.value = attr_string; |
896 | 0 | retmaj = gss_add_buffer_set_member(&retmin, &buf, attrs); |
897 | 0 | free(attr_string); |
898 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
899 | 0 | } |
900 | | |
901 | 0 | done: |
902 | 0 | if (retmaj) { |
903 | 0 | (void)gss_release_buffer_set(&tmpmin, attrs); |
904 | 0 | } |
905 | 0 | return GSSERRS(retmin, retmaj); |
906 | 0 | } |
907 | | |
908 | | /* RFC6680 - GSSAPI Naming Extensions */ |
909 | | uint32_t gssntlm_get_name_attribute(uint32_t *minor_status, |
910 | | gss_name_t name, |
911 | | gss_buffer_t attr, |
912 | | int *authenticated, |
913 | | int *complete, |
914 | | gss_buffer_t value, |
915 | | gss_buffer_t display_value, |
916 | | int *more) |
917 | 0 | { |
918 | 0 | uint32_t retmin; |
919 | 0 | uint32_t retmaj; |
920 | 0 | const struct gssntlm_name *in = (const struct gssntlm_name *)name; |
921 | 0 | struct gssntlm_name_attribute *found_attr; |
922 | |
|
923 | 0 | if (name == GSS_C_NO_NAME) { |
924 | 0 | return GSSERRS(GSS_S_BAD_NAME, GSS_S_CALL_INACCESSIBLE_READ); |
925 | 0 | } |
926 | 0 | if (attr == NULL) { |
927 | 0 | return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); |
928 | 0 | } |
929 | | |
930 | 0 | if (display_value) { |
931 | 0 | display_value->value = NULL; |
932 | 0 | display_value->length = 0; |
933 | 0 | } |
934 | 0 | if (more) { *more = 0; } |
935 | 0 | if (authenticated) { *authenticated = 0; } |
936 | 0 | if (complete) { *complete = 0; } |
937 | |
|
938 | 0 | found_attr = gssntlm_find_attr(in->attrs, attr->value, attr->length); |
939 | 0 | if (!found_attr) { |
940 | 0 | return GSSERRS(ENOENT, GSS_S_UNAVAILABLE); |
941 | 0 | } |
942 | | |
943 | 0 | if (authenticated) { *authenticated = 1; } |
944 | 0 | if (complete) { *complete = 1; } |
945 | 0 | if (value) { |
946 | 0 | gss_buffer_t attr_value = &found_attr->attr_value; |
947 | 0 | value->value = malloc(attr_value->length); |
948 | 0 | if (!value->value) { |
949 | 0 | return GSSERRS(ENOMEM, GSS_S_FAILURE); |
950 | 0 | } |
951 | 0 | memcpy(value->value, attr_value->value, attr_value->length); |
952 | 0 | value->length = attr_value->length; |
953 | 0 | } |
954 | 0 | return GSSERRS(0, GSS_S_COMPLETE); |
955 | 0 | } |
956 | | |
957 | | /* RFC5801 Extensions */ |
958 | | |
959 | 0 | #define GS2_NTLM_SASL_NAME "GS2-NTLM" |
960 | 0 | #define GS2_NTLM_SASL_NAME_LEN (sizeof(GS2_NTLM_SASL_NAME) - 1) |
961 | | |
962 | | uint32_t gssntlm_inquire_saslname_for_mech(OM_uint32 *minor_status, |
963 | | const gss_OID desired_mech, |
964 | | gss_buffer_t sasl_mech_name, |
965 | | gss_buffer_t mech_name, |
966 | | gss_buffer_t mech_description) |
967 | 0 | { |
968 | 0 | if (desired_mech && !gss_oid_equal(desired_mech, &gssntlm_oid)) { |
969 | 0 | *minor_status = ENOENT; |
970 | 0 | return GSS_S_BAD_MECH; |
971 | 0 | } |
972 | | |
973 | 0 | sasl_mech_name->value = NULL; |
974 | 0 | mech_name->value = NULL; |
975 | 0 | mech_description->value = NULL; |
976 | |
|
977 | 0 | *minor_status = ENOMEM; |
978 | |
|
979 | 0 | sasl_mech_name->value = strdup(GS2_NTLM_SASL_NAME); |
980 | 0 | if (sasl_mech_name->value == NULL) { |
981 | 0 | goto done; |
982 | 0 | } |
983 | 0 | sasl_mech_name->length = strlen(sasl_mech_name->value); |
984 | |
|
985 | 0 | mech_name->value = strdup("NTLM"); |
986 | 0 | if (mech_name->value == NULL) { |
987 | 0 | goto done; |
988 | 0 | } |
989 | 0 | mech_name->length = strlen(mech_name->value); |
990 | |
|
991 | 0 | mech_description->value = strdup("NTLM Mechanism"); |
992 | 0 | if (mech_name->value == NULL) { |
993 | 0 | goto done; |
994 | 0 | } |
995 | 0 | mech_description->length = strlen(mech_description->value); |
996 | |
|
997 | 0 | *minor_status = 0; |
998 | |
|
999 | 0 | done: |
1000 | 0 | if (*minor_status != 0) { |
1001 | 0 | free(sasl_mech_name->value); |
1002 | 0 | free(mech_name->value); |
1003 | 0 | free(mech_description->value); |
1004 | 0 | return GSS_S_FAILURE; |
1005 | 0 | } |
1006 | | |
1007 | 0 | return GSS_S_COMPLETE; |
1008 | 0 | } |
1009 | | |
1010 | | uint32_t gssntlm_inquire_mech_for_saslname(OM_uint32 *minor_status, |
1011 | | const gss_buffer_t sasl_mech_name, |
1012 | | gss_OID *mech_type) |
1013 | 0 | { |
1014 | 0 | if (sasl_mech_name->length == GS2_NTLM_SASL_NAME_LEN && |
1015 | 0 | memcmp(sasl_mech_name->value, |
1016 | 0 | GS2_NTLM_SASL_NAME, GS2_NTLM_SASL_NAME_LEN) == 0) { |
1017 | 0 | if (mech_type != NULL) { |
1018 | 0 | *mech_type = discard_const(&gssntlm_oid); |
1019 | 0 | } |
1020 | 0 | *minor_status = 0; |
1021 | 0 | return GSS_S_COMPLETE; |
1022 | 0 | } |
1023 | | |
1024 | 0 | *minor_status = ENOENT; |
1025 | 0 | return GSS_S_BAD_MECH; |
1026 | |
|
1027 | 0 | } |
1028 | | |
1029 | | static uint32_t make_ma_oid_set(uint32_t *minor_status, gss_OID_set *ma_set, |
1030 | | int supported) |
1031 | 0 | { |
1032 | 0 | gss_const_OID known_mech_attrs[] = { |
1033 | 0 | GSS_C_MA_MECH_CONCRETE, |
1034 | 0 | GSS_C_MA_MECH_PSEUDO, |
1035 | 0 | GSS_C_MA_MECH_COMPOSITE, |
1036 | 0 | GSS_C_MA_MECH_NEGO, |
1037 | 0 | GSS_C_MA_MECH_GLUE, |
1038 | 0 | GSS_C_MA_NOT_MECH, |
1039 | 0 | GSS_C_MA_DEPRECATED, |
1040 | 0 | GSS_C_MA_NOT_DFLT_MECH, |
1041 | 0 | GSS_C_MA_ITOK_FRAMED, |
1042 | 0 | GSS_C_MA_AUTH_INIT, |
1043 | 0 | GSS_C_MA_AUTH_TARG, |
1044 | 0 | GSS_C_MA_AUTH_INIT_INIT, |
1045 | 0 | GSS_C_MA_AUTH_TARG_INIT, |
1046 | 0 | GSS_C_MA_AUTH_INIT_ANON, |
1047 | 0 | GSS_C_MA_AUTH_TARG_ANON, |
1048 | 0 | GSS_C_MA_DELEG_CRED, |
1049 | 0 | GSS_C_MA_INTEG_PROT, |
1050 | 0 | GSS_C_MA_CONF_PROT, |
1051 | 0 | GSS_C_MA_MIC, |
1052 | 0 | GSS_C_MA_WRAP, |
1053 | 0 | GSS_C_MA_PROT_READY, |
1054 | 0 | GSS_C_MA_REPLAY_DET, |
1055 | 0 | GSS_C_MA_OOS_DET, |
1056 | 0 | GSS_C_MA_CBINDINGS, |
1057 | 0 | GSS_C_MA_PFS, |
1058 | 0 | GSS_C_MA_COMPRESS, |
1059 | 0 | GSS_C_MA_CTX_TRANS, |
1060 | 0 | NULL |
1061 | 0 | }; |
1062 | 0 | gss_const_OID supported_mech_attrs[] = { |
1063 | 0 | GSS_C_MA_MECH_CONCRETE, |
1064 | 0 | GSS_C_MA_AUTH_INIT, |
1065 | 0 | GSS_C_MA_INTEG_PROT, |
1066 | 0 | GSS_C_MA_CONF_PROT, |
1067 | 0 | GSS_C_MA_MIC, |
1068 | 0 | GSS_C_MA_WRAP, |
1069 | 0 | GSS_C_MA_OOS_DET, |
1070 | 0 | GSS_C_MA_CBINDINGS, |
1071 | 0 | GSS_C_MA_CTX_TRANS, |
1072 | 0 | NULL |
1073 | 0 | }; |
1074 | 0 | uint32_t maj = 0; |
1075 | 0 | uint32_t min = 0; |
1076 | 0 | gss_const_OID *array = known_mech_attrs; |
1077 | |
|
1078 | 0 | if (supported) { |
1079 | 0 | array = supported_mech_attrs; |
1080 | 0 | } |
1081 | |
|
1082 | 0 | maj = gss_create_empty_oid_set(&min, ma_set); |
1083 | 0 | if (maj != GSS_S_COMPLETE) { |
1084 | 0 | goto done; |
1085 | 0 | } |
1086 | 0 | for (int i = 0; array[i] != NULL; i++) { |
1087 | 0 | maj = gss_add_oid_set_member(&min, discard_const(array[i]), ma_set); |
1088 | 0 | if (maj != GSS_S_COMPLETE) { |
1089 | 0 | goto done; |
1090 | 0 | } |
1091 | 0 | } |
1092 | | |
1093 | 0 | done: |
1094 | 0 | *minor_status = min; |
1095 | 0 | return maj; |
1096 | 0 | } |
1097 | | |
1098 | | uint32_t gssntlm_inquire_attrs_for_mech(uint32_t *minor_status, |
1099 | | gss_const_OID mech_oid, |
1100 | | gss_OID_set *mech_attrs, |
1101 | | gss_OID_set *known_mech_attrs) |
1102 | 0 | { |
1103 | 0 | gss_OID_set s_ma = GSS_C_NULL_OID_SET; |
1104 | 0 | gss_OID_set k_ma = GSS_C_NULL_OID_SET; |
1105 | 0 | uint32_t maj = GSS_S_COMPLETE; |
1106 | 0 | uint32_t min = 0; |
1107 | |
|
1108 | 0 | if (mech_oid && !gss_oid_equal(mech_oid, &gssntlm_oid)) { |
1109 | 0 | *minor_status = ENOENT; |
1110 | 0 | return GSS_S_BAD_MECH; |
1111 | 0 | } |
1112 | | |
1113 | 0 | if (mech_attrs != NULL) { |
1114 | 0 | maj = make_ma_oid_set(&min, &s_ma, 1); |
1115 | 0 | if (maj != GSS_S_COMPLETE) { |
1116 | 0 | goto done; |
1117 | 0 | } |
1118 | 0 | } |
1119 | 0 | if (known_mech_attrs != NULL) { |
1120 | 0 | maj = make_ma_oid_set(&min, &k_ma, 0); |
1121 | 0 | if (maj != GSS_S_COMPLETE) { |
1122 | 0 | goto done; |
1123 | 0 | } |
1124 | 0 | } |
1125 | | |
1126 | 0 | done: |
1127 | 0 | if (maj != GSS_S_COMPLETE) { |
1128 | 0 | gss_release_oid_set(&min, &s_ma); |
1129 | 0 | gss_release_oid_set(&min, &k_ma); |
1130 | 0 | } |
1131 | 0 | if (mech_attrs != NULL) { |
1132 | 0 | *mech_attrs = s_ma; |
1133 | 0 | } |
1134 | 0 | if (known_mech_attrs != NULL) { |
1135 | 0 | *known_mech_attrs = k_ma; |
1136 | 0 | } |
1137 | |
|
1138 | 0 | *minor_status = min; |
1139 | 0 | return maj; |
1140 | 0 | } |