Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : RFC2478 Compliant SPNEGO implementation
5 :
6 : Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "includes.h"
24 : #include "../libcli/auth/spnego.h"
25 : #include "../lib/util/asn1.h"
26 :
27 40089 : static bool read_negTokenInit(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
28 : struct spnego_negTokenInit *token)
29 : {
30 40089 : ZERO_STRUCTP(token);
31 :
32 40089 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
33 40089 : if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
34 :
35 151258 : while (asn1_tag_remaining(asn1) > 0) {
36 : int i;
37 : uint8_t context;
38 :
39 80177 : if (!asn1_peek_uint8(asn1, &context)) {
40 0 : asn1_set_error(asn1);
41 0 : break;
42 : }
43 :
44 80177 : switch (context) {
45 : /* Read mechTypes */
46 40089 : case ASN1_CONTEXT(0): {
47 : const char **mechTypes;
48 :
49 40089 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
50 40089 : if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
51 :
52 40089 : mechTypes = talloc(mem_ctx, const char *);
53 40089 : if (mechTypes == NULL) {
54 0 : asn1_set_error(asn1);
55 0 : return false;
56 : }
57 239014 : for (i = 0; asn1_tag_remaining(asn1) > 0; i++) {
58 : char *oid;
59 : const char **p;
60 94126 : p = talloc_realloc(mem_ctx,
61 : mechTypes,
62 : const char *, i+2);
63 94126 : if (p == NULL) {
64 0 : talloc_free(mechTypes);
65 0 : asn1_set_error(asn1);
66 0 : return false;
67 : }
68 94126 : mechTypes = p;
69 :
70 94126 : if (!asn1_read_OID(asn1, mechTypes, &oid)) return false;
71 94126 : mechTypes[i] = oid;
72 : }
73 40089 : mechTypes[i] = NULL;
74 40089 : token->mechTypes = mechTypes;
75 :
76 40089 : asn1_end_tag(asn1);
77 40089 : asn1_end_tag(asn1);
78 40089 : break;
79 : }
80 : /* Read reqFlags */
81 0 : case ASN1_CONTEXT(1):
82 0 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
83 0 : if (!asn1_read_BitString(asn1, mem_ctx, &token->reqFlags,
84 0 : &token->reqFlagsPadding)) return false;
85 0 : if (!asn1_end_tag(asn1)) return false;
86 0 : break;
87 : /* Read mechToken */
88 32145 : case ASN1_CONTEXT(2):
89 32145 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false;
90 32145 : if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechToken)) return false;
91 32145 : if (!asn1_end_tag(asn1)) return false;
92 32145 : break;
93 : /* Read mecListMIC */
94 7943 : case ASN1_CONTEXT(3):
95 : {
96 : uint8_t type_peek;
97 7943 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false;
98 7943 : if (!asn1_peek_uint8(asn1, &type_peek)) {
99 0 : asn1_set_error(asn1);
100 2004 : break;
101 : }
102 7943 : if (type_peek == ASN1_OCTET_STRING) {
103 0 : if (!asn1_read_OctetString(asn1, mem_ctx,
104 0 : &token->mechListMIC)) return false;
105 : } else {
106 : /* RFC 2478 says we have an Octet String here,
107 : but W2k sends something different... */
108 : char *mechListMIC;
109 7943 : if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
110 7943 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
111 7943 : if (!asn1_read_GeneralString(asn1, mem_ctx, &mechListMIC)) return false;
112 7943 : if (!asn1_end_tag(asn1)) return false;
113 7943 : if (!asn1_end_tag(asn1)) return false;
114 :
115 7943 : token->targetPrincipal = mechListMIC;
116 : }
117 7943 : if (!asn1_end_tag(asn1)) return false;
118 7943 : break;
119 : }
120 0 : default:
121 0 : asn1_set_error(asn1);
122 0 : break;
123 : }
124 : }
125 :
126 40089 : if (!asn1_end_tag(asn1)) return false;
127 40089 : if (!asn1_end_tag(asn1)) return false;
128 :
129 40089 : return !asn1_has_error(asn1);
130 : }
131 :
132 43482 : static bool write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
133 : {
134 43482 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
135 43482 : if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
136 :
137 : /* Write mechTypes */
138 43482 : if (token->mechTypes && *token->mechTypes) {
139 : int i;
140 :
141 43482 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
142 43482 : if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
143 147537 : for (i = 0; token->mechTypes[i]; i++) {
144 104055 : if (!asn1_write_OID(asn1, token->mechTypes[i])) return false;
145 : }
146 43482 : if (!asn1_pop_tag(asn1)) return false;
147 43482 : if (!asn1_pop_tag(asn1)) return false;
148 : }
149 :
150 : /* write reqFlags */
151 43482 : if (token->reqFlags.length > 0) {
152 0 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
153 0 : if (!asn1_write_BitString(asn1, token->reqFlags.data,
154 : token->reqFlags.length,
155 0 : token->reqFlagsPadding)) return false;
156 0 : if (!asn1_pop_tag(asn1)) return false;
157 : }
158 :
159 : /* write mechToken */
160 43482 : if (token->mechToken.data) {
161 32348 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false;
162 32348 : if (!asn1_write_OctetString(asn1, token->mechToken.data,
163 0 : token->mechToken.length)) return false;
164 32348 : if (!asn1_pop_tag(asn1)) return false;
165 : }
166 :
167 : /* write mechListMIC */
168 43482 : if (token->mechListMIC.data) {
169 11133 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(3))) return false;
170 : #if 0
171 : /* This is what RFC 2478 says ... */
172 : asn1_write_OctetString(asn1, token->mechListMIC.data,
173 : token->mechListMIC.length);
174 : #else
175 : /* ... but unfortunately this is what Windows
176 : sends/expects */
177 11133 : if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
178 11133 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
179 11133 : if (!asn1_push_tag(asn1, ASN1_GENERAL_STRING)) return false;
180 11133 : if (!asn1_write(asn1, token->mechListMIC.data,
181 11133 : token->mechListMIC.length)) return false;
182 11133 : if (!asn1_pop_tag(asn1)) return false;
183 11133 : if (!asn1_pop_tag(asn1)) return false;
184 11133 : if (!asn1_pop_tag(asn1)) return false;
185 : #endif
186 11133 : if (!asn1_pop_tag(asn1)) return false;
187 : }
188 :
189 43482 : if (!asn1_pop_tag(asn1)) return false;
190 43482 : if (!asn1_pop_tag(asn1)) return false;
191 :
192 43482 : return !asn1_has_error(asn1);
193 : }
194 :
195 58986 : static bool read_negTokenTarg(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
196 : struct spnego_negTokenTarg *token)
197 : {
198 58986 : ZERO_STRUCTP(token);
199 :
200 58986 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
201 58986 : if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
202 :
203 253268 : while (asn1_tag_remaining(asn1) > 0) {
204 : uint8_t context;
205 : uint8_t neg_result;
206 : char *oid;
207 :
208 148719 : if (!asn1_peek_uint8(asn1, &context)) {
209 0 : asn1_set_error(asn1);
210 0 : break;
211 : }
212 :
213 148719 : switch (context) {
214 45174 : case ASN1_CONTEXT(0):
215 45174 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
216 45174 : if (!asn1_start_tag(asn1, ASN1_ENUMERATED)) return false;
217 45174 : if (!asn1_read_uint8(asn1, &neg_result)) return false;
218 45174 : token->negResult = neg_result;
219 45174 : if (!asn1_end_tag(asn1)) return false;
220 45174 : if (!asn1_end_tag(asn1)) return false;
221 45174 : break;
222 31713 : case ASN1_CONTEXT(1):
223 31713 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
224 31713 : if (!asn1_read_OID(asn1, mem_ctx, &oid)) return false;
225 31713 : token->supportedMech = oid;
226 31713 : if (!asn1_end_tag(asn1)) return false;
227 31713 : break;
228 45522 : case ASN1_CONTEXT(2):
229 45522 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false;
230 45522 : if (!asn1_read_OctetString(asn1, mem_ctx, &token->responseToken)) return false;
231 45522 : if (!asn1_end_tag(asn1)) return false;
232 45522 : break;
233 26310 : case ASN1_CONTEXT(3):
234 26310 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false;
235 26310 : if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechListMIC)) return false;
236 26310 : if (!asn1_end_tag(asn1)) return false;
237 26310 : break;
238 0 : default:
239 0 : asn1_set_error(asn1);
240 0 : break;
241 : }
242 : }
243 :
244 58986 : if (!asn1_end_tag(asn1)) return false;
245 58986 : if (!asn1_end_tag(asn1)) return false;
246 :
247 58986 : return !asn1_has_error(asn1);
248 : }
249 :
250 59298 : static bool write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
251 : {
252 59298 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
253 59298 : if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
254 :
255 59298 : if (token->negResult != SPNEGO_NONE_RESULT) {
256 45530 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
257 45530 : if (!asn1_write_enumerated(asn1, token->negResult)) return false;
258 45530 : if (!asn1_pop_tag(asn1)) return false;
259 : }
260 :
261 59298 : if (token->supportedMech) {
262 31949 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
263 31949 : if (!asn1_write_OID(asn1, token->supportedMech)) return false;
264 31949 : if (!asn1_pop_tag(asn1)) return false;
265 : }
266 :
267 59298 : if (token->responseToken.data) {
268 45714 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false;
269 45714 : if (!asn1_write_OctetString(asn1, token->responseToken.data,
270 0 : token->responseToken.length)) return false;
271 45714 : if (!asn1_pop_tag(asn1)) return false;
272 : }
273 :
274 59298 : if (token->mechListMIC.data) {
275 26312 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(3))) return false;
276 26312 : if (!asn1_write_OctetString(asn1, token->mechListMIC.data,
277 0 : token->mechListMIC.length)) return false;
278 26312 : if (!asn1_pop_tag(asn1)) return false;
279 : }
280 :
281 59298 : if (!asn1_pop_tag(asn1)) return false;
282 59298 : if (!asn1_pop_tag(asn1)) return false;
283 :
284 59298 : return !asn1_has_error(asn1);
285 : }
286 :
287 99298 : ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data *token)
288 : {
289 : struct asn1_data *asn1;
290 99298 : ssize_t ret = -1;
291 : uint8_t context;
292 :
293 99298 : ZERO_STRUCTP(token);
294 :
295 99298 : if (data.length == 0) {
296 0 : return ret;
297 : }
298 :
299 99298 : asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
300 99298 : if (asn1 == NULL) {
301 0 : return -1;
302 : }
303 :
304 99298 : if (!asn1_load(asn1, data)) goto err;
305 :
306 99298 : if (!asn1_peek_uint8(asn1, &context)) {
307 0 : asn1_set_error(asn1);
308 : } else {
309 99298 : switch (context) {
310 40279 : case ASN1_APPLICATION(0):
311 40279 : if (!asn1_start_tag(asn1, ASN1_APPLICATION(0))) goto err;
312 40279 : if (!asn1_check_OID(asn1, OID_SPNEGO)) goto err;
313 40089 : if (read_negTokenInit(asn1, mem_ctx, &token->negTokenInit)) {
314 40089 : token->type = SPNEGO_NEG_TOKEN_INIT;
315 : }
316 40089 : if (!asn1_end_tag(asn1)) goto err;
317 40089 : break;
318 58986 : case ASN1_CONTEXT(1):
319 58986 : if (read_negTokenTarg(asn1, mem_ctx, &token->negTokenTarg)) {
320 58986 : token->type = SPNEGO_NEG_TOKEN_TARG;
321 : }
322 58986 : break;
323 33 : default:
324 33 : asn1_set_error(asn1);
325 33 : break;
326 : }
327 : }
328 :
329 99108 : if (!asn1_has_error(asn1)) {
330 99075 : ret = asn1_current_ofs(asn1);
331 : }
332 :
333 76792 : err:
334 :
335 99298 : asn1_free(asn1);
336 :
337 99298 : return ret;
338 : }
339 :
340 102780 : ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
341 : {
342 102780 : struct asn1_data *asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
343 102780 : ssize_t ret = -1;
344 :
345 102780 : if (asn1 == NULL) {
346 0 : return -1;
347 : }
348 :
349 102780 : switch (spnego->type) {
350 43482 : case SPNEGO_NEG_TOKEN_INIT:
351 43482 : if (!asn1_push_tag(asn1, ASN1_APPLICATION(0))) goto err;
352 43482 : if (!asn1_write_OID(asn1, OID_SPNEGO)) goto err;
353 43482 : if (!write_negTokenInit(asn1, &spnego->negTokenInit)) goto err;
354 43482 : if (!asn1_pop_tag(asn1)) goto err;
355 43482 : break;
356 59298 : case SPNEGO_NEG_TOKEN_TARG:
357 59298 : write_negTokenTarg(asn1, &spnego->negTokenTarg);
358 59298 : break;
359 0 : default:
360 0 : asn1_set_error(asn1);
361 0 : break;
362 : }
363 :
364 102780 : if (!asn1_extract_blob(asn1, mem_ctx, blob)) {
365 0 : goto err;
366 : }
367 :
368 102780 : ret = asn1_current_ofs(asn1);
369 :
370 102780 : err:
371 :
372 102780 : asn1_free(asn1);
373 :
374 102780 : return ret;
375 : }
376 :
377 0 : bool spnego_free_data(struct spnego_data *spnego)
378 : {
379 0 : bool ret = true;
380 :
381 0 : if (!spnego) goto out;
382 :
383 0 : switch(spnego->type) {
384 0 : case SPNEGO_NEG_TOKEN_INIT:
385 0 : if (spnego->negTokenInit.mechTypes) {
386 0 : talloc_free(discard_const(spnego->negTokenInit.mechTypes));
387 : }
388 0 : data_blob_free(&spnego->negTokenInit.reqFlags);
389 0 : data_blob_free(&spnego->negTokenInit.mechToken);
390 0 : data_blob_free(&spnego->negTokenInit.mechListMIC);
391 0 : talloc_free(spnego->negTokenInit.targetPrincipal);
392 0 : break;
393 0 : case SPNEGO_NEG_TOKEN_TARG:
394 0 : if (spnego->negTokenTarg.supportedMech) {
395 0 : talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
396 : }
397 0 : data_blob_free(&spnego->negTokenTarg.responseToken);
398 0 : data_blob_free(&spnego->negTokenTarg.mechListMIC);
399 0 : break;
400 0 : default:
401 0 : ret = false;
402 0 : break;
403 : }
404 0 : ZERO_STRUCTP(spnego);
405 0 : out:
406 0 : return ret;
407 : }
408 :
409 75628 : bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
410 : const char * const *mech_types,
411 : DATA_BLOB *blob)
412 : {
413 75628 : bool ret = false;
414 75628 : struct asn1_data *asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
415 :
416 75628 : if (asn1 == NULL) {
417 0 : return false;
418 : }
419 :
420 : /* Write mechTypes */
421 75628 : if (mech_types && *mech_types) {
422 : int i;
423 :
424 75628 : if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) goto err;
425 250138 : for (i = 0; mech_types[i]; i++) {
426 174510 : if (!asn1_write_OID(asn1, mech_types[i])) goto err;
427 : }
428 75628 : if (!asn1_pop_tag(asn1)) goto err;
429 : }
430 :
431 75628 : if (asn1_has_error(asn1)) {
432 0 : goto err;
433 : }
434 :
435 75628 : if (!asn1_extract_blob(asn1, mem_ctx, blob)) {
436 0 : goto err;
437 : }
438 :
439 75628 : ret = true;
440 :
441 75628 : err:
442 :
443 75628 : asn1_free(asn1);
444 :
445 75628 : return ret;
446 : }
|