/src/gss-ntlmssp/src/gss_serialize.c
Line | Count | Source |
1 | | /* Copyright 2013 Simo Sorce <simo@samba.org>, see COPYING for license */ |
2 | | |
3 | | #include <endian.h> |
4 | | #include <errno.h> |
5 | | #include <stdint.h> |
6 | | #include <stdlib.h> |
7 | | #include <string.h> |
8 | | #include <time.h> |
9 | | |
10 | | #include "gssapi_ntlmssp.h" |
11 | | #include "gss_ntlmssp.h" |
12 | | |
13 | | /* each integer in the export format is a little endian integer */ |
14 | | #pragma pack(push, 1) |
15 | | struct relmem { |
16 | | uint32_t ptr; |
17 | | uint32_t len; |
18 | | }; |
19 | | |
20 | | struct export_attrs { |
21 | | uint16_t count; |
22 | | /* for each count there is a pair of name/value buffers |
23 | | * that we'll pack in a single buffer */ |
24 | | struct relmem buffers; |
25 | | }; |
26 | | |
27 | | struct export_name { |
28 | | uint8_t type; |
29 | | struct relmem dom_or_spn; |
30 | | struct relmem name; |
31 | | struct export_attrs attrs; |
32 | | }; |
33 | | |
34 | | struct export_keys { |
35 | | struct relmem sign_key; |
36 | | struct relmem seal_key; |
37 | | struct relmem rc4_state; |
38 | | uint32_t seq_num; |
39 | | }; |
40 | | |
41 | | #define EXPORT_CTX_VER 0x0005 |
42 | | struct export_ctx { |
43 | | uint16_t version; |
44 | | uint8_t role; |
45 | | uint8_t stage; |
46 | | uint8_t sec_req; |
47 | | |
48 | | struct relmem workstation; |
49 | | |
50 | | struct relmem nego_msg; |
51 | | struct relmem chal_msg; |
52 | | struct relmem auth_msg; |
53 | | |
54 | | struct export_name source; |
55 | | struct export_name target; |
56 | | |
57 | | uint8_t server_chal[8]; |
58 | | |
59 | | uint32_t gss_flags; |
60 | | uint32_t neg_flags; |
61 | | |
62 | | struct relmem exported_session_key; |
63 | | struct export_keys send; |
64 | | struct export_keys recv; |
65 | | |
66 | | uint8_t int_flags; |
67 | | uint64_t expration_time; |
68 | | |
69 | | uint8_t data[]; |
70 | | }; |
71 | | #pragma pack(pop) |
72 | | |
73 | 0 | #define EXP_CTX_CLIENT 1 |
74 | 0 | #define EXP_CTX_SERVER 2 |
75 | 0 | #define EXP_CTX_DOMSRV 3 |
76 | 0 | #define EXP_CTX_DOMCTR 4 |
77 | 0 | #define EXP_STG_INIT 1 |
78 | 0 | #define EXP_STG_NEGO 2 |
79 | 0 | #define EXP_STG_CHAL 3 |
80 | 0 | #define EXP_STG_AUTH 4 |
81 | 0 | #define EXP_STG_DONE 5 |
82 | 0 | #define EXP_NAME_NONE 0 |
83 | 0 | #define EXP_NAME_ANON 1 |
84 | 0 | #define EXP_NAME_USER 2 |
85 | 0 | #define EXP_NAME_SERV 3 |
86 | | |
87 | 0 | #define INC_EXP_SIZE 0x001000 /* 4K */ |
88 | 0 | #define MAX_EXP_SIZE 0x100000 /* 1M */ |
89 | | |
90 | | #define NEW_SIZE(s, n) \ |
91 | 0 | ((((s) + (n) + (INC_EXP_SIZE-1)) / INC_EXP_SIZE) * INC_EXP_SIZE) |
92 | | |
93 | | struct export_state { |
94 | | uint8_t *exp_struct; |
95 | | size_t exp_size; |
96 | | size_t exp_data; |
97 | | size_t exp_len; |
98 | | }; |
99 | | |
100 | | #define RELMEM_PTR(state, rm) \ |
101 | 0 | ((state)->exp_struct + (state)->exp_data + (rm)->ptr) |
102 | | |
103 | | #define RELMEM_ZERO(rm) \ |
104 | 0 | memset((rm), 0, sizeof(struct relmem)) |
105 | | |
106 | | static int export_data_allocate(struct export_state *state, |
107 | | size_t length, struct relmem *rm) |
108 | 0 | { |
109 | 0 | size_t new_size; |
110 | 0 | void *tmp; |
111 | |
|
112 | 0 | if (length > MAX_EXP_SIZE) { |
113 | 0 | return E2BIG; |
114 | 0 | } |
115 | | |
116 | 0 | if (length > state->exp_size - state->exp_len) { |
117 | 0 | new_size = NEW_SIZE(state->exp_len, length); |
118 | 0 | if ((new_size < state->exp_size) || new_size > MAX_EXP_SIZE) { |
119 | 0 | return E2BIG; |
120 | 0 | } |
121 | 0 | tmp = realloc(state->exp_struct, new_size); |
122 | 0 | if (!tmp) { |
123 | 0 | return ENOMEM; |
124 | 0 | } |
125 | 0 | state->exp_struct = tmp; |
126 | 0 | state->exp_size = new_size; |
127 | 0 | } |
128 | | |
129 | 0 | rm->ptr = state->exp_len - state->exp_data; |
130 | 0 | rm->len = length; |
131 | 0 | state->exp_len += length; |
132 | |
|
133 | 0 | return 0; |
134 | 0 | } |
135 | | |
136 | | static int export_data_buffer(struct export_state *state, |
137 | | void *data, size_t length, |
138 | | struct relmem *rm) |
139 | 0 | { |
140 | 0 | int ret; |
141 | |
|
142 | 0 | if (length == 0) { |
143 | 0 | RELMEM_ZERO(rm); |
144 | 0 | return 0; |
145 | 0 | } |
146 | | |
147 | 0 | ret = export_data_allocate(state, length, rm); |
148 | 0 | if (ret) return ret; |
149 | | |
150 | 0 | memcpy(RELMEM_PTR(state, rm), data, length); |
151 | 0 | return 0; |
152 | 0 | } |
153 | | |
154 | | static int export_attrs(struct export_state *state, |
155 | | struct gssntlm_name_attribute *attrs, |
156 | | struct export_attrs *exp_attrs) |
157 | 0 | { |
158 | 0 | size_t count = gssntlm_get_attrs_count(attrs); |
159 | 0 | size_t ptr_array_size = 0; |
160 | 0 | int ret; |
161 | |
|
162 | 0 | if (count == 0) return 0; |
163 | 0 | if (count > UINT16_MAX) return E2BIG; |
164 | | |
165 | 0 | exp_attrs->count = count; |
166 | | |
167 | | /* reserve data space in state->exp_struct for pointers */ |
168 | 0 | ptr_array_size = count * 2 * sizeof(struct relmem); |
169 | 0 | ret = export_data_allocate(state, ptr_array_size, &exp_attrs->buffers); |
170 | 0 | if (ret) return ret; |
171 | | |
172 | | /* exp_attrs->buffers may be reallocated as part of data structure |
173 | | * expansion in export_data_buffer() so we need to recompute the |
174 | | * buffers pointer after each use of export_data_buffer */ |
175 | 0 | for (size_t i = 0; i < count; i++) { |
176 | 0 | struct relmem *buffers; |
177 | 0 | struct relmem buffer; |
178 | | /* name */ |
179 | 0 | ret = export_data_buffer(state, attrs[i].attr_name, |
180 | 0 | strlen(attrs[i].attr_name), &buffer); |
181 | 0 | if (ret) return ret; |
182 | 0 | buffers = (struct relmem *)RELMEM_PTR(state, &exp_attrs->buffers); |
183 | 0 | memcpy(&buffers[i * 2], &buffer, sizeof(struct relmem)); |
184 | | /* value */ |
185 | 0 | ret = export_data_buffer(state, attrs[i].attr_value.value, |
186 | 0 | attrs[i].attr_value.length, &buffer); |
187 | 0 | if (ret) return ret; |
188 | 0 | buffers = (struct relmem *)RELMEM_PTR(state, &exp_attrs->buffers); |
189 | 0 | memcpy(&buffers[i * 2 + 1], &buffer, sizeof(struct relmem)); |
190 | 0 | } |
191 | | |
192 | 0 | return 0; |
193 | 0 | } |
194 | | |
195 | | static int export_name(struct export_state *state, |
196 | | struct gssntlm_name *name, |
197 | | struct export_name *exp_name) |
198 | 0 | { |
199 | 0 | int ret; |
200 | |
|
201 | 0 | memset(exp_name, 0, sizeof(struct export_name)); |
202 | |
|
203 | 0 | switch (name->type) { |
204 | 0 | case GSSNTLM_NAME_NULL: |
205 | 0 | break; |
206 | 0 | case GSSNTLM_NAME_ANON: |
207 | 0 | exp_name->type = EXP_NAME_ANON; |
208 | 0 | break; |
209 | 0 | case GSSNTLM_NAME_USER: |
210 | 0 | exp_name->type = EXP_NAME_USER; |
211 | 0 | if (name->data.user.domain) { |
212 | 0 | ret = export_data_buffer(state, name->data.user.domain, |
213 | 0 | strlen(name->data.user.domain), |
214 | 0 | &exp_name->dom_or_spn); |
215 | 0 | if (ret) { |
216 | 0 | return ret; |
217 | 0 | } |
218 | 0 | } |
219 | 0 | if (name->data.user.name) { |
220 | 0 | ret = export_data_buffer(state, name->data.user.name, |
221 | 0 | strlen(name->data.user.name), |
222 | 0 | &exp_name->name); |
223 | 0 | if (ret) { |
224 | 0 | return ret; |
225 | 0 | } |
226 | 0 | } |
227 | 0 | break; |
228 | 0 | case GSSNTLM_NAME_SERVER: |
229 | 0 | exp_name->type = EXP_NAME_SERV; |
230 | 0 | if (name->data.server.spn) { |
231 | 0 | ret = export_data_buffer(state, name->data.server.spn, |
232 | 0 | strlen(name->data.server.spn), |
233 | 0 | &exp_name->dom_or_spn); |
234 | 0 | if (ret) { |
235 | 0 | return ret; |
236 | 0 | } |
237 | 0 | } |
238 | 0 | if (name->data.server.name) { |
239 | 0 | ret = export_data_buffer(state, name->data.server.name, |
240 | 0 | strlen(name->data.server.name), |
241 | 0 | &exp_name->name); |
242 | 0 | if (ret) { |
243 | 0 | return ret; |
244 | 0 | } |
245 | 0 | } |
246 | 0 | break; |
247 | 0 | default: |
248 | 0 | return EINVAL; |
249 | 0 | } |
250 | 0 | return export_attrs(state, name->attrs, &exp_name->attrs); |
251 | 0 | } |
252 | | |
253 | | static int export_keys(struct export_state *state, |
254 | | struct ntlm_signseal_handle *keys, |
255 | | struct export_keys *exp_keys) |
256 | 0 | { |
257 | 0 | uint8_t buf[258*sizeof(uint32_t)]; |
258 | 0 | struct ntlm_buffer out = { .data=buf, .length=sizeof(buf) }; |
259 | 0 | int ret; |
260 | |
|
261 | 0 | memset(exp_keys, 0, sizeof(struct export_keys)); |
262 | |
|
263 | 0 | if (keys->sign_key.length > 0) { |
264 | 0 | ret = export_data_buffer(state, |
265 | 0 | keys->sign_key.data, |
266 | 0 | keys->sign_key.length, |
267 | 0 | &exp_keys->sign_key); |
268 | 0 | if (ret) return ret; |
269 | 0 | } |
270 | | |
271 | 0 | if (keys->seal_key.length > 0) { |
272 | 0 | ret = export_data_buffer(state, |
273 | 0 | keys->seal_key.data, |
274 | 0 | keys->seal_key.length, |
275 | 0 | &exp_keys->seal_key); |
276 | 0 | if (ret) return ret; |
277 | 0 | } |
278 | | |
279 | 0 | if (keys->seal_handle) { |
280 | 0 | ret = RC4_EXPORT(keys->seal_handle, &out); |
281 | 0 | if (ret) return ret; |
282 | 0 | ret = export_data_buffer(state, buf, sizeof(buf), |
283 | 0 | &exp_keys->rc4_state); |
284 | 0 | safezero(buf, sizeof(buf)); |
285 | 0 | if (ret) return ret; |
286 | 0 | } |
287 | | |
288 | 0 | exp_keys->seq_num = htole32(keys->seq_num); |
289 | |
|
290 | 0 | return 0; |
291 | 0 | } |
292 | | |
293 | | uint32_t gssntlm_export_sec_context(uint32_t *minor_status, |
294 | | gss_ctx_id_t *context_handle, |
295 | | gss_buffer_t interprocess_token) |
296 | 0 | { |
297 | 0 | struct gssntlm_ctx *ctx; |
298 | 0 | struct export_state state = { 0 }; |
299 | 0 | struct export_ctx ectx = { 0 }; |
300 | 0 | uint64_t expiration; |
301 | 0 | uint32_t retmaj; |
302 | 0 | uint32_t retmin; |
303 | 0 | int ret; |
304 | |
|
305 | 0 | if (context_handle == NULL) { |
306 | 0 | return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); |
307 | 0 | } |
308 | | |
309 | 0 | if (interprocess_token == NULL) { |
310 | 0 | return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE); |
311 | 0 | } |
312 | | |
313 | 0 | ctx = (struct gssntlm_ctx *)*context_handle; |
314 | 0 | if (ctx == NULL) return GSSERRS(ERR_BADARG, GSS_S_NO_CONTEXT); |
315 | | |
316 | 0 | if (ctx->expiration_time && ctx->expiration_time < time(NULL)) { |
317 | 0 | return GSSERRS(ERR_EXPIRED, GSS_S_CONTEXT_EXPIRED); |
318 | 0 | } |
319 | | |
320 | | /* we want to leave space to add the basic context structure in the buffer |
321 | | * however we want a memory stable structure we can refernce via memory |
322 | | * pointers while we run export functions for all the "static" context |
323 | | * data, so we allocate space but we use a stack allocated struct until |
324 | | * the very end. */ |
325 | 0 | state.exp_size = NEW_SIZE(0, sizeof(struct export_ctx)); |
326 | 0 | state.exp_struct = malloc(state.exp_size); |
327 | 0 | if (!state.exp_struct) { |
328 | 0 | set_GSSERR(ENOMEM); |
329 | 0 | goto done; |
330 | 0 | } |
331 | 0 | state.exp_data = (uint8_t *)&ectx.data - (uint8_t *)&ectx; |
332 | 0 | state.exp_len = state.exp_data; |
333 | |
|
334 | 0 | ectx.version = htole16(EXPORT_CTX_VER); |
335 | |
|
336 | 0 | switch(ctx->role) { |
337 | 0 | case GSSNTLM_CLIENT: |
338 | 0 | ectx.role = EXP_CTX_CLIENT; |
339 | 0 | break; |
340 | 0 | case GSSNTLM_SERVER: |
341 | 0 | ectx.role = EXP_CTX_SERVER; |
342 | 0 | break; |
343 | 0 | case GSSNTLM_DOMAIN_SERVER: |
344 | 0 | ectx.role = EXP_CTX_DOMSRV; |
345 | 0 | break; |
346 | 0 | case GSSNTLM_DOMAIN_CONTROLLER: |
347 | 0 | ectx.role = EXP_CTX_DOMCTR; |
348 | 0 | break; |
349 | 0 | } |
350 | | |
351 | 0 | switch(ctx->stage) { |
352 | 0 | case NTLMSSP_STAGE_INIT: |
353 | 0 | ectx.stage = EXP_STG_INIT; |
354 | 0 | break; |
355 | 0 | case NTLMSSP_STAGE_NEGOTIATE: |
356 | 0 | ectx.stage = EXP_STG_NEGO; |
357 | 0 | break; |
358 | 0 | case NTLMSSP_STAGE_CHALLENGE: |
359 | 0 | ectx.stage = EXP_STG_CHAL; |
360 | 0 | break; |
361 | 0 | case NTLMSSP_STAGE_AUTHENTICATE: |
362 | 0 | ectx.stage = EXP_STG_AUTH; |
363 | 0 | break; |
364 | 0 | case NTLMSSP_STAGE_DONE: |
365 | 0 | ectx.stage = EXP_STG_DONE; |
366 | 0 | break; |
367 | 0 | } |
368 | | |
369 | 0 | ectx.sec_req = ctx->sec_req; |
370 | |
|
371 | 0 | if (!ctx->workstation) { |
372 | 0 | RELMEM_ZERO(&ectx.workstation); |
373 | 0 | } else { |
374 | 0 | ret = export_data_buffer(&state, ctx->workstation, |
375 | 0 | strlen(ctx->workstation), |
376 | 0 | &ectx.workstation); |
377 | 0 | if (ret) { |
378 | 0 | set_GSSERR(ret); |
379 | 0 | goto done; |
380 | 0 | } |
381 | 0 | } |
382 | | |
383 | 0 | if (ctx->nego_msg.length > 0) { |
384 | 0 | ret = export_data_buffer(&state, |
385 | 0 | ctx->nego_msg.data, |
386 | 0 | ctx->nego_msg.length, |
387 | 0 | &ectx.nego_msg); |
388 | 0 | if (ret) { |
389 | 0 | set_GSSERR(ret); |
390 | 0 | goto done; |
391 | 0 | } |
392 | 0 | } else { |
393 | 0 | RELMEM_ZERO(&ectx.nego_msg); |
394 | 0 | } |
395 | | |
396 | 0 | if (ctx->chal_msg.length > 0) { |
397 | 0 | ret = export_data_buffer(&state, |
398 | 0 | ctx->chal_msg.data, |
399 | 0 | ctx->chal_msg.length, |
400 | 0 | &ectx.chal_msg); |
401 | 0 | if (ret) { |
402 | 0 | set_GSSERR(ret); |
403 | 0 | goto done; |
404 | 0 | } |
405 | 0 | } else { |
406 | 0 | RELMEM_ZERO(&ectx.chal_msg); |
407 | 0 | } |
408 | | |
409 | 0 | if (ctx->auth_msg.length > 0) { |
410 | 0 | ret = export_data_buffer(&state, |
411 | 0 | ctx->auth_msg.data, |
412 | 0 | ctx->auth_msg.length, |
413 | 0 | &ectx.auth_msg); |
414 | 0 | if (ret) { |
415 | 0 | set_GSSERR(ret); |
416 | 0 | goto done; |
417 | 0 | } |
418 | 0 | } else { |
419 | 0 | RELMEM_ZERO(&ectx.auth_msg); |
420 | 0 | } |
421 | | |
422 | 0 | ret = export_name(&state, &ctx->source_name, &ectx.source); |
423 | 0 | if (ret) { |
424 | 0 | set_GSSERR(ret); |
425 | 0 | goto done; |
426 | 0 | } |
427 | | |
428 | 0 | ret = export_name(&state, &ctx->target_name, &ectx.target); |
429 | 0 | if (ret) { |
430 | 0 | set_GSSERR(ret); |
431 | 0 | goto done; |
432 | 0 | } |
433 | | |
434 | 0 | memcpy(ectx.server_chal, ctx->server_chal, 8); |
435 | |
|
436 | 0 | ectx.gss_flags = htole32(ctx->gss_flags); |
437 | 0 | ectx.neg_flags = htole32(ctx->neg_flags); |
438 | |
|
439 | 0 | ret = export_data_buffer(&state, |
440 | 0 | ctx->exported_session_key.data, |
441 | 0 | ctx->exported_session_key.length, |
442 | 0 | &ectx.exported_session_key); |
443 | 0 | if (ret) { |
444 | 0 | set_GSSERR(ret); |
445 | 0 | goto done; |
446 | 0 | } |
447 | | |
448 | 0 | ret = export_keys(&state, &ctx->crypto_state.send, &ectx.send); |
449 | 0 | if (ret) { |
450 | 0 | set_GSSERR(ret); |
451 | 0 | goto done; |
452 | 0 | } |
453 | | |
454 | 0 | ret = export_keys(&state, &ctx->crypto_state.recv, &ectx.recv); |
455 | 0 | if (ret) { |
456 | 0 | set_GSSERR(ret); |
457 | 0 | goto done; |
458 | 0 | } |
459 | | |
460 | 0 | ectx.int_flags = ctx->int_flags; |
461 | |
|
462 | 0 | expiration = ctx->expiration_time; |
463 | 0 | ectx.expration_time = htole64(expiration); |
464 | | |
465 | | /* finally copy ectx into the allocated buffer */ |
466 | 0 | memcpy(state.exp_struct, &ectx, state.exp_data); |
467 | |
|
468 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
469 | |
|
470 | 0 | done: |
471 | 0 | if (retmaj) { |
472 | 0 | free(state.exp_struct); |
473 | 0 | } else { |
474 | 0 | uint32_t min; |
475 | 0 | interprocess_token->value = state.exp_struct; |
476 | 0 | interprocess_token->length = state.exp_len; |
477 | | |
478 | | /* Invalidate the current context once successfully exported */ |
479 | 0 | gssntlm_delete_sec_context(&min, context_handle, NULL); |
480 | 0 | } |
481 | 0 | return GSSERR(); |
482 | 0 | } |
483 | | |
484 | | static uint32_t import_data_buffer(uint32_t *minor_status, |
485 | | struct export_state *state, |
486 | | uint8_t **dest, size_t *len, bool alloc, |
487 | | struct relmem *rm, bool str) |
488 | 0 | { |
489 | 0 | uint32_t retmaj; |
490 | 0 | uint32_t retmin; |
491 | 0 | void *ptr; |
492 | |
|
493 | 0 | if (str && !alloc) { |
494 | 0 | return EINVAL; |
495 | 0 | } |
496 | | |
497 | 0 | if (rm->len == 0) { |
498 | 0 | if (alloc) { |
499 | 0 | *dest = NULL; |
500 | 0 | } |
501 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
502 | 0 | goto done; |
503 | 0 | } |
504 | | |
505 | 0 | if (state->exp_data + rm->ptr + rm->len > state->exp_len) { |
506 | 0 | set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN); |
507 | 0 | goto done; |
508 | 0 | } |
509 | 0 | ptr = RELMEM_PTR(state, rm); |
510 | 0 | if (alloc) { |
511 | 0 | if (str) { |
512 | 0 | *dest = (uint8_t *)strndup((const char *)ptr, rm->len); |
513 | 0 | } else { |
514 | 0 | *dest = malloc(rm->len); |
515 | 0 | if (*dest) { |
516 | 0 | memcpy(*dest, ptr, rm->len); |
517 | 0 | } |
518 | 0 | } |
519 | 0 | if (!*dest) { |
520 | 0 | set_GSSERR(ENOMEM); |
521 | 0 | goto done; |
522 | 0 | } |
523 | 0 | } else { |
524 | 0 | if (!*len) { |
525 | 0 | set_GSSERR(ERR_BADARG); |
526 | 0 | goto done; |
527 | 0 | } |
528 | 0 | if (rm->len > *len) { |
529 | 0 | set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN); |
530 | 0 | goto done; |
531 | 0 | } |
532 | 0 | memcpy(*dest, ptr, rm->len); |
533 | 0 | } |
534 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
535 | |
|
536 | 0 | done: |
537 | 0 | if (retmaj == GSS_S_COMPLETE) { |
538 | 0 | if (len) *len = rm->len; |
539 | 0 | } |
540 | 0 | return GSSERR(); |
541 | 0 | } |
542 | | |
543 | | static uint32_t import_attrs(uint32_t *minor_status, |
544 | | struct export_state *state, |
545 | | struct export_attrs *attrs, |
546 | | struct gssntlm_name_attribute **imp_attrs) |
547 | 0 | { |
548 | 0 | struct gssntlm_name_attribute *a; |
549 | 0 | uint32_t retmaj = GSS_S_COMPLETE; |
550 | 0 | uint32_t retmin = 0; |
551 | 0 | uint8_t *cursor; |
552 | |
|
553 | 0 | if (attrs->count == 0) goto done; |
554 | | |
555 | 0 | a = calloc(attrs->count + 1, sizeof(struct gssntlm_name_attribute)); |
556 | 0 | if (a == NULL) { |
557 | 0 | set_GSSERR(ENOMEM); |
558 | 0 | goto done; |
559 | 0 | } |
560 | 0 | *imp_attrs = a; |
561 | |
|
562 | 0 | cursor = RELMEM_PTR(state, &attrs->buffers); |
563 | |
|
564 | 0 | for (size_t i = 0; i < attrs->count; i++) { |
565 | 0 | struct relmem name; |
566 | 0 | struct relmem value; |
567 | 0 | memcpy(&name, cursor, sizeof(struct relmem)); |
568 | 0 | cursor += sizeof(struct relmem); |
569 | 0 | memcpy(&value, cursor, sizeof(struct relmem)); |
570 | 0 | cursor += sizeof(struct relmem); |
571 | 0 | retmaj = import_data_buffer(&retmin, state, |
572 | 0 | (uint8_t **)&a[i].attr_name, |
573 | 0 | NULL, true, &name, true); |
574 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
575 | 0 | retmaj = import_data_buffer(&retmin, state, |
576 | 0 | (uint8_t **)&a[i].attr_value.value, |
577 | 0 | &a[i].attr_value.length, |
578 | 0 | true, &value, false); |
579 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
580 | 0 | } |
581 | | |
582 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
583 | |
|
584 | 0 | done: |
585 | 0 | return GSSERR(); |
586 | 0 | } |
587 | | |
588 | | static uint32_t import_name(uint32_t *minor_status, |
589 | | struct export_state *state, |
590 | | struct export_name *name, |
591 | | struct gssntlm_name *imp_name) |
592 | 0 | { |
593 | 0 | uint32_t retmaj; |
594 | 0 | uint32_t retmin; |
595 | 0 | uint8_t *dest; |
596 | |
|
597 | 0 | switch (name->type) { |
598 | 0 | case EXP_NAME_NONE: |
599 | 0 | memset(imp_name, 0, sizeof(struct gssntlm_name)); |
600 | 0 | break; |
601 | | |
602 | 0 | case EXP_NAME_ANON: |
603 | 0 | memset(imp_name, 0, sizeof(struct gssntlm_name)); |
604 | 0 | imp_name->type = GSSNTLM_NAME_ANON; |
605 | 0 | break; |
606 | | |
607 | 0 | case EXP_NAME_USER: |
608 | 0 | imp_name->type = GSSNTLM_NAME_USER; |
609 | 0 | dest = NULL; |
610 | 0 | if (name->dom_or_spn.len > 0) { |
611 | 0 | retmaj = import_data_buffer(&retmin, state, |
612 | 0 | &dest, NULL, true, |
613 | 0 | &name->dom_or_spn, true); |
614 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
615 | 0 | } |
616 | 0 | imp_name->data.user.domain = (char *)dest; |
617 | 0 | dest = NULL; |
618 | 0 | if (name->name.len > 0) { |
619 | 0 | retmaj = import_data_buffer(&retmin, state, |
620 | 0 | &dest, NULL, true, |
621 | 0 | &name->name, true); |
622 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
623 | 0 | } |
624 | 0 | imp_name->data.user.name = (char *)dest; |
625 | 0 | break; |
626 | | |
627 | 0 | case EXP_NAME_SERV: |
628 | 0 | imp_name->type = GSSNTLM_NAME_SERVER; |
629 | 0 | dest = NULL; |
630 | 0 | if (name->dom_or_spn.len > 0) { |
631 | 0 | retmaj = import_data_buffer(&retmin, state, |
632 | 0 | &dest, NULL, true, |
633 | 0 | &name->dom_or_spn, true); |
634 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
635 | 0 | } |
636 | 0 | imp_name->data.server.spn = (char *)dest; |
637 | 0 | dest = NULL; |
638 | 0 | if (name->name.len > 0) { |
639 | 0 | retmaj = import_data_buffer(&retmin, state, |
640 | 0 | &dest, NULL, true, |
641 | 0 | &name->name, true); |
642 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
643 | 0 | } |
644 | 0 | imp_name->data.server.name = (char *)dest; |
645 | 0 | break; |
646 | | |
647 | 0 | default: |
648 | 0 | set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN); |
649 | 0 | break; |
650 | 0 | } |
651 | | |
652 | 0 | retmaj = import_attrs(minor_status, state, &name->attrs, &imp_name->attrs); |
653 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
654 | | |
655 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
656 | |
|
657 | 0 | done: |
658 | 0 | return GSSERR(); |
659 | 0 | } |
660 | | |
661 | | static uint32_t import_keys(uint32_t *minor_status, |
662 | | struct export_state *state, |
663 | | struct export_keys *keys, |
664 | | struct ntlm_signseal_handle *imp_keys) |
665 | 0 | { |
666 | 0 | struct ntlm_buffer in; |
667 | 0 | uint8_t *dest; |
668 | 0 | uint32_t retmaj; |
669 | 0 | uint32_t retmin; |
670 | 0 | int ret; |
671 | |
|
672 | 0 | if (keys->sign_key.len > 0) { |
673 | 0 | imp_keys->sign_key.length = 16; /* buf max size */ |
674 | 0 | dest = imp_keys->sign_key.data; |
675 | 0 | retmaj = import_data_buffer(&retmin, state, |
676 | 0 | &dest, &imp_keys->sign_key.length, |
677 | 0 | false, &keys->sign_key, false); |
678 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
679 | 0 | } else { |
680 | 0 | memset(&imp_keys->sign_key, 0, sizeof(struct ntlm_key)); |
681 | 0 | } |
682 | | |
683 | 0 | if (keys->seal_key.len > 0) { |
684 | 0 | imp_keys->seal_key.length = 16; /* buf max size */ |
685 | 0 | dest = imp_keys->seal_key.data; |
686 | 0 | retmaj = import_data_buffer(&retmin, state, |
687 | 0 | &dest, &imp_keys->seal_key.length, |
688 | 0 | false, &keys->seal_key, false); |
689 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
690 | 0 | } else { |
691 | 0 | memset(&imp_keys->seal_key, 0, sizeof(struct ntlm_key)); |
692 | 0 | } |
693 | | |
694 | 0 | if (keys->rc4_state.len > 0) { |
695 | 0 | retmaj = import_data_buffer(&retmin, state, |
696 | 0 | &in.data, &in.length, true, |
697 | 0 | &keys->rc4_state, false); |
698 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
699 | 0 | ret = RC4_IMPORT(&imp_keys->seal_handle, &in); |
700 | 0 | safezero(in.data, in.length); |
701 | 0 | safefree(in.data); |
702 | 0 | if (ret) { |
703 | 0 | set_GSSERR(ret); |
704 | 0 | goto done; |
705 | 0 | } |
706 | 0 | } else { |
707 | 0 | imp_keys->seal_handle = NULL; |
708 | 0 | } |
709 | | |
710 | 0 | imp_keys->seq_num = le32toh(keys->seq_num); |
711 | |
|
712 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
713 | |
|
714 | 0 | done: |
715 | 0 | return GSSERR(); |
716 | 0 | } |
717 | | |
718 | | uint32_t gssntlm_import_sec_context(uint32_t *minor_status, |
719 | | gss_buffer_t interprocess_token, |
720 | | gss_ctx_id_t *context_handle) |
721 | 0 | { |
722 | 0 | struct gssntlm_ctx *ctx = NULL; |
723 | 0 | struct export_state state; |
724 | 0 | struct export_ctx *ectx; |
725 | 0 | uint8_t *dest; |
726 | 0 | uint64_t time; |
727 | 0 | uint32_t retmaj; |
728 | 0 | uint32_t retmin; |
729 | |
|
730 | 0 | if (interprocess_token == NULL) { |
731 | 0 | return GSSERRS(0, GSS_S_CALL_INACCESSIBLE_READ); |
732 | 0 | } |
733 | | |
734 | 0 | if (interprocess_token->length < sizeof(struct export_ctx)) { |
735 | 0 | return GSSERRS(0, GSS_S_DEFECTIVE_TOKEN); |
736 | 0 | } |
737 | | |
738 | 0 | if (context_handle == NULL) { |
739 | 0 | return GSSERRS(0, GSS_S_CALL_INACCESSIBLE_WRITE); |
740 | 0 | } |
741 | | |
742 | 0 | ctx = calloc(1, sizeof(struct gssntlm_ctx)); |
743 | 0 | if (!ctx) { |
744 | 0 | set_GSSERR(ENOMEM); |
745 | 0 | goto done; |
746 | 0 | } |
747 | 0 | retmin = ntlm_init_ctx(&ctx->ntlm); |
748 | 0 | if (retmin) { |
749 | 0 | set_GSSERR(retmin); |
750 | 0 | goto done; |
751 | 0 | } |
752 | | |
753 | 0 | state.exp_struct = interprocess_token->value; |
754 | 0 | state.exp_len = interprocess_token->length; |
755 | 0 | ectx = (struct export_ctx *)state.exp_struct; |
756 | 0 | state.exp_data = (uint8_t *)ectx->data - (uint8_t *)ectx; |
757 | |
|
758 | 0 | if (ectx->version != le16toh(EXPORT_CTX_VER)) { |
759 | 0 | set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN); |
760 | 0 | goto done; |
761 | 0 | } |
762 | | |
763 | 0 | switch (ectx->role) { |
764 | 0 | case EXP_CTX_CLIENT: |
765 | 0 | ctx->role = GSSNTLM_CLIENT; |
766 | 0 | break; |
767 | 0 | case EXP_CTX_SERVER: |
768 | 0 | ctx->role = GSSNTLM_SERVER; |
769 | 0 | break; |
770 | 0 | case EXP_CTX_DOMSRV: |
771 | 0 | ctx->role = GSSNTLM_DOMAIN_SERVER; |
772 | 0 | break; |
773 | 0 | case EXP_CTX_DOMCTR: |
774 | 0 | ctx->role = GSSNTLM_DOMAIN_CONTROLLER; |
775 | 0 | break; |
776 | 0 | default: |
777 | 0 | set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN); |
778 | 0 | goto done; |
779 | 0 | } |
780 | | |
781 | 0 | switch (ectx->stage) { |
782 | 0 | case EXP_STG_INIT: |
783 | 0 | ctx->stage = NTLMSSP_STAGE_INIT; |
784 | 0 | break; |
785 | 0 | case EXP_STG_NEGO: |
786 | 0 | ctx->stage = NTLMSSP_STAGE_NEGOTIATE; |
787 | 0 | break; |
788 | 0 | case EXP_STG_CHAL: |
789 | 0 | ctx->stage = NTLMSSP_STAGE_CHALLENGE; |
790 | 0 | break; |
791 | 0 | case EXP_STG_AUTH: |
792 | 0 | ctx->stage = NTLMSSP_STAGE_AUTHENTICATE; |
793 | 0 | break; |
794 | 0 | case EXP_STG_DONE: |
795 | 0 | ctx->stage = NTLMSSP_STAGE_DONE; |
796 | 0 | break; |
797 | 0 | default: |
798 | 0 | set_GSSERRS(0, GSS_S_DEFECTIVE_TOKEN); |
799 | 0 | goto done; |
800 | 0 | } |
801 | | |
802 | 0 | ctx->sec_req = ectx->sec_req; |
803 | |
|
804 | 0 | dest = NULL; |
805 | 0 | if (ectx->workstation.len > 0) { |
806 | 0 | retmaj = import_data_buffer(&retmin, &state, &dest, NULL, |
807 | 0 | true, &ectx->workstation, true); |
808 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
809 | 0 | } |
810 | 0 | ctx->workstation = (char *)dest; |
811 | |
|
812 | 0 | if (ectx->nego_msg.len > 0) { |
813 | 0 | retmaj = import_data_buffer(&retmin, &state, |
814 | 0 | &ctx->nego_msg.data, &ctx->nego_msg.length, |
815 | 0 | true, &ectx->nego_msg, false); |
816 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
817 | 0 | } else { |
818 | 0 | ctx->nego_msg.data = NULL; |
819 | 0 | ctx->nego_msg.length = 0; |
820 | 0 | } |
821 | | |
822 | 0 | if (ectx->chal_msg.len > 0) { |
823 | 0 | retmaj = import_data_buffer(&retmin, &state, |
824 | 0 | &ctx->chal_msg.data, &ctx->chal_msg.length, |
825 | 0 | true, &ectx->chal_msg, false); |
826 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
827 | 0 | } else { |
828 | 0 | ctx->chal_msg.data = NULL; |
829 | 0 | ctx->chal_msg.length = 0; |
830 | 0 | } |
831 | | |
832 | 0 | if (ectx->auth_msg.len > 0) { |
833 | 0 | retmaj = import_data_buffer(&retmin, &state, |
834 | 0 | &ctx->auth_msg.data, &ctx->auth_msg.length, |
835 | 0 | true, &ectx->auth_msg, false); |
836 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
837 | 0 | } else { |
838 | 0 | ctx->auth_msg.data = NULL; |
839 | 0 | ctx->auth_msg.length = 0; |
840 | 0 | } |
841 | | |
842 | 0 | retmaj = import_name(&retmin, &state, |
843 | 0 | &ectx->source, &ctx->source_name); |
844 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
845 | | |
846 | 0 | retmaj = import_name(&retmin, &state, |
847 | 0 | &ectx->target, &ctx->target_name); |
848 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
849 | | |
850 | 0 | memcpy(ctx->server_chal, ectx->server_chal, 8); |
851 | |
|
852 | 0 | ctx->gss_flags = le32toh(ectx->gss_flags); |
853 | 0 | ctx->neg_flags = le32toh(ectx->neg_flags); |
854 | |
|
855 | 0 | if (ectx->exported_session_key.len > 0) { |
856 | 0 | ctx->exported_session_key.length = 16; /* buf max size */ |
857 | 0 | dest = ctx->exported_session_key.data; |
858 | 0 | retmaj = import_data_buffer(&retmin, &state, &dest, |
859 | 0 | &ctx->exported_session_key.length, |
860 | 0 | false, &ectx->exported_session_key, false); |
861 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
862 | 0 | } else { |
863 | 0 | memset(&ctx->exported_session_key, 0, sizeof(struct ntlm_key)); |
864 | 0 | } |
865 | | |
866 | 0 | retmaj = import_keys(&retmin, &state, |
867 | 0 | &ectx->send, &ctx->crypto_state.send); |
868 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
869 | | |
870 | 0 | retmaj = import_keys(&retmin, &state, |
871 | 0 | &ectx->recv, &ctx->crypto_state.recv); |
872 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
873 | | |
874 | | /* We need to restoer also the general crypto status flags */ |
875 | 0 | ctx->crypto_state.ext_sec = |
876 | 0 | (ctx->neg_flags & NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY); |
877 | 0 | ctx->crypto_state.datagram = |
878 | 0 | (ctx->neg_flags & NTLMSSP_NEGOTIATE_DATAGRAM); |
879 | |
|
880 | 0 | ctx->int_flags = ectx->int_flags; |
881 | |
|
882 | 0 | time = le64toh(ectx->expration_time); |
883 | 0 | ctx->expiration_time = time; |
884 | |
|
885 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
886 | |
|
887 | 0 | done: |
888 | 0 | if (retmaj == GSS_S_COMPLETE) { |
889 | 0 | *context_handle = (gss_ctx_id_t)ctx; |
890 | 0 | } else { |
891 | 0 | uint32_t min; |
892 | 0 | gssntlm_delete_sec_context(&min, (gss_ctx_id_t *)&ctx, NULL); |
893 | 0 | } |
894 | 0 | return GSSERR(); |
895 | 0 | } |
896 | | |
897 | | #define EXPORT_CRED_VER 0x0002 |
898 | | |
899 | | #pragma pack(push, 1) |
900 | | struct export_cred { |
901 | | uint16_t version; |
902 | | uint16_t type; |
903 | | |
904 | | struct export_name name; /* user or server name */ |
905 | | struct relmem nt_hash; /* empty for dummy or server */ |
906 | | struct relmem lm_hash; /* empty for dummy or server */ |
907 | | struct relmem keyfile; |
908 | | uint8_t ext_cached; |
909 | | |
910 | | uint8_t data[]; |
911 | | }; |
912 | | #pragma pack(pop) |
913 | | |
914 | 0 | #define EXP_CRED_NONE 0 |
915 | 0 | #define EXP_CRED_ANON 1 |
916 | 0 | #define EXP_CRED_USER 2 |
917 | 0 | #define EXP_CRED_SERVER 3 |
918 | 0 | #define EXP_CRED_EXTERNAL 4 |
919 | | |
920 | | uint32_t gssntlm_export_cred(uint32_t *minor_status, |
921 | | gss_cred_id_t cred_handle, |
922 | | gss_buffer_t token) |
923 | 0 | { |
924 | 0 | struct gssntlm_cred *cred; |
925 | 0 | struct export_state state = { 0 }; |
926 | 0 | struct export_cred ecred = { 0 }; |
927 | 0 | uint32_t retmaj; |
928 | 0 | uint32_t retmin; |
929 | 0 | int ret; |
930 | |
|
931 | 0 | if (token == NULL) { |
932 | 0 | return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE); |
933 | 0 | } |
934 | | |
935 | 0 | cred = (struct gssntlm_cred *)cred_handle; |
936 | 0 | if (cred_handle == NULL) { |
937 | 0 | return GSSERRS(ERR_NOARG, GSS_S_NO_CRED); |
938 | 0 | } |
939 | | |
940 | | /* we want to leave space to add the basic creds structure in the buffer |
941 | | * however we want a memory stable structure we can refernce via memory |
942 | | * pointers while we run export functions for all the "static" context |
943 | | * data, so we allocate space but we use a stack allocated struct until |
944 | | * the very end. */ |
945 | 0 | state.exp_size = NEW_SIZE(0, sizeof(struct export_cred)); |
946 | 0 | state.exp_struct = calloc(1, state.exp_size); |
947 | 0 | if (!state.exp_struct) { |
948 | 0 | set_GSSERR(ENOMEM); |
949 | 0 | goto done; |
950 | 0 | } |
951 | 0 | state.exp_data = (uint8_t *)&ecred.data - (uint8_t *)&ecred; |
952 | 0 | state.exp_len = state.exp_data; |
953 | |
|
954 | 0 | ecred.version = htole16(EXPORT_CRED_VER); |
955 | |
|
956 | 0 | switch (cred->type) { |
957 | 0 | case GSSNTLM_CRED_NONE: |
958 | 0 | ecred.type = EXP_CRED_NONE; |
959 | 0 | break; |
960 | 0 | case GSSNTLM_CRED_ANON: |
961 | 0 | ecred.type = EXP_CRED_ANON; |
962 | 0 | break; |
963 | 0 | case GSSNTLM_CRED_USER: |
964 | 0 | ecred.type = EXP_CRED_USER; |
965 | |
|
966 | 0 | ret = export_name(&state, &cred->cred.user.user, &ecred.name); |
967 | 0 | if (ret) { |
968 | 0 | set_GSSERR(ret); |
969 | 0 | goto done; |
970 | 0 | } |
971 | | |
972 | 0 | ret = export_data_buffer(&state, |
973 | 0 | cred->cred.user.nt_hash.data, |
974 | 0 | cred->cred.user.nt_hash.length, |
975 | 0 | &ecred.nt_hash); |
976 | 0 | if (ret) { |
977 | 0 | set_GSSERR(ret); |
978 | 0 | goto done; |
979 | 0 | } |
980 | | |
981 | 0 | ret = export_data_buffer(&state, |
982 | 0 | cred->cred.user.lm_hash.data, |
983 | 0 | cred->cred.user.lm_hash.length, |
984 | 0 | &ecred.lm_hash); |
985 | 0 | if (ret) { |
986 | 0 | set_GSSERR(ret); |
987 | 0 | goto done; |
988 | 0 | } |
989 | 0 | break; |
990 | 0 | case GSSNTLM_CRED_SERVER: |
991 | 0 | ecred.type = EXP_CRED_SERVER; |
992 | |
|
993 | 0 | ret = export_name(&state, &cred->cred.server.name, &ecred.name); |
994 | 0 | if (ret) { |
995 | 0 | set_GSSERR(ret); |
996 | 0 | goto done; |
997 | 0 | } |
998 | | |
999 | 0 | if (cred->cred.server.keyfile) { |
1000 | 0 | ret = export_data_buffer(&state, |
1001 | 0 | cred->cred.server.keyfile, |
1002 | 0 | strlen(cred->cred.server.keyfile), |
1003 | 0 | &ecred.keyfile); |
1004 | 0 | if (ret) { |
1005 | 0 | set_GSSERR(ret); |
1006 | 0 | goto done; |
1007 | 0 | } |
1008 | 0 | } |
1009 | 0 | break; |
1010 | 0 | case GSSNTLM_CRED_EXTERNAL: |
1011 | 0 | ecred.type = EXP_CRED_EXTERNAL; |
1012 | |
|
1013 | 0 | ret = export_name(&state, &cred->cred.external.user, &ecred.name); |
1014 | 0 | if (ret) { |
1015 | 0 | set_GSSERR(ret); |
1016 | 0 | goto done; |
1017 | 0 | } |
1018 | 0 | if (cred->cred.external.creds_in_cache) { |
1019 | 0 | ecred.ext_cached = 1; |
1020 | 0 | } |
1021 | 0 | break; |
1022 | |
|
1023 | 0 | } |
1024 | | |
1025 | | /* finally copy ecred into the allocated buffer */ |
1026 | 0 | memcpy(state.exp_struct, &ecred, state.exp_data); |
1027 | |
|
1028 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
1029 | |
|
1030 | 0 | done: |
1031 | 0 | if (retmaj) { |
1032 | 0 | free(state.exp_struct); |
1033 | 0 | } else { |
1034 | 0 | token->value = state.exp_struct; |
1035 | 0 | token->length = state.exp_len; |
1036 | 0 | } |
1037 | 0 | return GSSERR(); |
1038 | 0 | } |
1039 | | |
1040 | | uint32_t gssntlm_import_cred(uint32_t *minor_status, |
1041 | | gss_buffer_t token, |
1042 | | gss_cred_id_t *cred_handle) |
1043 | 0 | { |
1044 | 0 | struct gssntlm_cred *cred; |
1045 | 0 | struct export_state state = { 0 }; |
1046 | 0 | struct export_cred *ecred; |
1047 | 0 | uint32_t retmaj; |
1048 | 0 | uint32_t retmin; |
1049 | |
|
1050 | 0 | if (token == NULL) { |
1051 | 0 | return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_READ); |
1052 | 0 | } |
1053 | | |
1054 | 0 | if (token->length < sizeof(struct export_cred)) { |
1055 | 0 | return GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN); |
1056 | 0 | } |
1057 | | |
1058 | 0 | if (cred_handle == NULL) { |
1059 | 0 | return GSSERRS(ERR_NOARG, GSS_S_CALL_INACCESSIBLE_WRITE); |
1060 | 0 | } |
1061 | | |
1062 | 0 | cred = calloc(1, sizeof(struct gssntlm_cred)); |
1063 | 0 | if (!cred) { |
1064 | 0 | set_GSSERR(ENOMEM); |
1065 | 0 | goto done; |
1066 | 0 | } |
1067 | | |
1068 | 0 | state.exp_struct = token->value; |
1069 | 0 | state.exp_len = token->length; |
1070 | 0 | ecred = (struct export_cred *)state.exp_struct; |
1071 | 0 | state.exp_data = (char *)ecred->data - (char *)ecred; |
1072 | |
|
1073 | 0 | if (ecred->version != le16toh(EXPORT_CRED_VER)) { |
1074 | 0 | set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN); |
1075 | 0 | goto done; |
1076 | 0 | } |
1077 | | |
1078 | 0 | switch (ecred->type) { |
1079 | 0 | case EXP_CRED_NONE: |
1080 | 0 | cred->type = GSSNTLM_CRED_NONE; |
1081 | 0 | break; |
1082 | 0 | case EXP_CRED_ANON: |
1083 | 0 | cred->type = GSSNTLM_CRED_ANON; |
1084 | 0 | break; |
1085 | 0 | case EXP_CRED_USER: |
1086 | 0 | cred->type = GSSNTLM_CRED_USER; |
1087 | 0 | retmaj = import_name(&retmin, &state, &ecred->name, |
1088 | 0 | &cred->cred.user.user); |
1089 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
1090 | | |
1091 | 0 | if (ecred->nt_hash.len > 16 || ecred->lm_hash.len > 16) { |
1092 | 0 | set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN); |
1093 | 0 | goto done; |
1094 | 0 | } |
1095 | | |
1096 | 0 | retmaj = import_data_buffer(&retmin, &state, |
1097 | 0 | (uint8_t **)&cred->cred.user.nt_hash.data, |
1098 | 0 | &cred->cred.user.nt_hash.length, |
1099 | 0 | false, &ecred->nt_hash, false); |
1100 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
1101 | | |
1102 | 0 | retmaj = import_data_buffer(&retmin, &state, |
1103 | 0 | (uint8_t **)&cred->cred.user.lm_hash.data, |
1104 | 0 | &cred->cred.user.lm_hash.length, |
1105 | 0 | false, &ecred->lm_hash, false); |
1106 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
1107 | 0 | break; |
1108 | 0 | case EXP_CRED_SERVER: |
1109 | 0 | cred->type = GSSNTLM_CRED_SERVER; |
1110 | 0 | retmaj = import_name(&retmin, &state, &ecred->name, |
1111 | 0 | &cred->cred.server.name); |
1112 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
1113 | 0 | if (ecred->keyfile.len > 0) { |
1114 | 0 | retmaj = import_data_buffer(&retmin, &state, |
1115 | 0 | (uint8_t **)&cred->cred.server.keyfile, |
1116 | 0 | NULL, true, &ecred->keyfile, true); |
1117 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
1118 | 0 | } |
1119 | 0 | break; |
1120 | 0 | case EXP_CRED_EXTERNAL: |
1121 | 0 | cred->type = GSSNTLM_CRED_EXTERNAL; |
1122 | 0 | retmaj = import_name(&retmin, &state, &ecred->name, |
1123 | 0 | &cred->cred.external.user); |
1124 | 0 | if (retmaj != GSS_S_COMPLETE) goto done; |
1125 | 0 | cred->cred.external.creds_in_cache = (ecred->ext_cached == 1); |
1126 | 0 | break; |
1127 | 0 | default: |
1128 | 0 | set_GSSERRS(ERR_BADARG, GSS_S_DEFECTIVE_TOKEN); |
1129 | 0 | break; |
1130 | 0 | } |
1131 | | |
1132 | 0 | set_GSSERRS(0, GSS_S_COMPLETE); |
1133 | |
|
1134 | 0 | done: |
1135 | 0 | if (retmaj == GSS_S_COMPLETE) { |
1136 | 0 | *cred_handle = (gss_cred_id_t)cred; |
1137 | 0 | } else { |
1138 | 0 | uint32_t min; |
1139 | 0 | gssntlm_release_cred(&min, (gss_cred_id_t *)&cred); |
1140 | 0 | } |
1141 | 0 | return GSSERR(); |
1142 | 0 | } |