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 108407 : static bool read_negTokenInit(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
28 : struct spnego_negTokenInit *token)
29 : {
30 108407 : ZERO_STRUCTP(token);
31 :
32 108407 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
33 108407 : if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
34 :
35 325215 : while (asn1_tag_remaining(asn1) > 0) {
36 4368 : int i;
37 4368 : uint8_t context;
38 :
39 216808 : if (!asn1_peek_uint8(asn1, &context)) {
40 0 : asn1_set_error(asn1);
41 0 : break;
42 : }
43 :
44 216808 : switch (context) {
45 : /* Read mechTypes */
46 108407 : case ASN1_CONTEXT(0): {
47 2184 : const char **mechTypes;
48 :
49 108407 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
50 108407 : if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
51 :
52 108407 : mechTypes = talloc(mem_ctx, const char *);
53 108407 : if (mechTypes == NULL) {
54 0 : asn1_set_error(asn1);
55 0 : return false;
56 : }
57 305255 : for (i = 0; asn1_tag_remaining(asn1) > 0; i++) {
58 6074 : char *oid;
59 6074 : const char **p;
60 196848 : p = talloc_realloc(mem_ctx,
61 : mechTypes,
62 : const char *, i+2);
63 196848 : if (p == NULL) {
64 0 : talloc_free(mechTypes);
65 0 : asn1_set_error(asn1);
66 0 : return false;
67 : }
68 196848 : mechTypes = p;
69 :
70 196848 : if (!asn1_read_OID(asn1, mechTypes, &oid)) return false;
71 196848 : mechTypes[i] = oid;
72 : }
73 108407 : mechTypes[i] = NULL;
74 108407 : token->mechTypes = mechTypes;
75 :
76 108407 : asn1_end_tag(asn1);
77 108407 : asn1_end_tag(asn1);
78 108407 : 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 69641 : case ASN1_CONTEXT(2):
89 69641 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false;
90 69641 : if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechToken)) return false;
91 69641 : if (!asn1_end_tag(asn1)) return false;
92 68588 : break;
93 : /* Read mecListMIC */
94 38760 : case ASN1_CONTEXT(3):
95 : {
96 1131 : uint8_t type_peek;
97 38760 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false;
98 38760 : if (!asn1_peek_uint8(asn1, &type_peek)) {
99 0 : asn1_set_error(asn1);
100 38760 : break;
101 : }
102 38760 : 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 1131 : char *mechListMIC;
109 38760 : if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
110 38760 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
111 38760 : if (!asn1_read_GeneralString(asn1, mem_ctx, &mechListMIC)) return false;
112 38760 : if (!asn1_end_tag(asn1)) return false;
113 38760 : if (!asn1_end_tag(asn1)) return false;
114 :
115 38760 : token->targetPrincipal = mechListMIC;
116 : }
117 38760 : if (!asn1_end_tag(asn1)) return false;
118 37629 : break;
119 : }
120 0 : default:
121 0 : asn1_set_error(asn1);
122 0 : break;
123 : }
124 : }
125 :
126 108407 : if (!asn1_end_tag(asn1)) return false;
127 108407 : if (!asn1_end_tag(asn1)) return false;
128 :
129 108407 : return !asn1_has_error(asn1);
130 : }
131 :
132 122903 : static bool write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
133 : {
134 122903 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
135 122903 : if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
136 :
137 : /* Write mechTypes */
138 122903 : if (token->mechTypes && *token->mechTypes) {
139 2651 : int i;
140 :
141 122903 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
142 122903 : if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
143 342243 : for (i = 0; token->mechTypes[i]; i++) {
144 219340 : if (!asn1_write_OID(asn1, token->mechTypes[i])) return false;
145 : }
146 122903 : if (!asn1_pop_tag(asn1)) return false;
147 122903 : if (!asn1_pop_tag(asn1)) return false;
148 : }
149 :
150 : /* write reqFlags */
151 122903 : 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 122903 : if (token->mechToken.data) {
161 71352 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false;
162 71352 : if (!asn1_write_OctetString(asn1, token->mechToken.data,
163 0 : token->mechToken.length)) return false;
164 71352 : if (!asn1_pop_tag(asn1)) return false;
165 : }
166 :
167 : /* write mechListMIC */
168 122903 : if (token->mechListMIC.data) {
169 51545 : 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 51545 : if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
178 51545 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
179 51545 : if (!asn1_push_tag(asn1, ASN1_GENERAL_STRING)) return false;
180 51545 : if (!asn1_write(asn1, token->mechListMIC.data,
181 51545 : token->mechListMIC.length)) return false;
182 51545 : if (!asn1_pop_tag(asn1)) return false;
183 51545 : if (!asn1_pop_tag(asn1)) return false;
184 51545 : if (!asn1_pop_tag(asn1)) return false;
185 : #endif
186 51545 : if (!asn1_pop_tag(asn1)) return false;
187 : }
188 :
189 122903 : if (!asn1_pop_tag(asn1)) return false;
190 122903 : if (!asn1_pop_tag(asn1)) return false;
191 :
192 122903 : return !asn1_has_error(asn1);
193 : }
194 :
195 148951 : static bool read_negTokenTarg(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
196 : struct spnego_negTokenTarg *token)
197 : {
198 148951 : ZERO_STRUCTP(token);
199 :
200 148951 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
201 148951 : if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
202 :
203 513483 : while (asn1_tag_remaining(asn1) > 0) {
204 4140 : uint8_t context;
205 4140 : uint8_t neg_result;
206 4140 : char *oid;
207 :
208 364532 : if (!asn1_peek_uint8(asn1, &context)) {
209 0 : asn1_set_error(asn1);
210 0 : break;
211 : }
212 :
213 364532 : switch (context) {
214 107253 : case ASN1_CONTEXT(0):
215 107253 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
216 107253 : if (!asn1_start_tag(asn1, ASN1_ENUMERATED)) return false;
217 107253 : if (!asn1_read_uint8(asn1, &neg_result)) return false;
218 107253 : token->negResult = neg_result;
219 107253 : if (!asn1_end_tag(asn1)) return false;
220 107253 : if (!asn1_end_tag(asn1)) return false;
221 105950 : break;
222 69303 : case ASN1_CONTEXT(1):
223 69303 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
224 69303 : if (!asn1_read_OID(asn1, mem_ctx, &oid)) return false;
225 69303 : token->supportedMech = oid;
226 69303 : if (!asn1_end_tag(asn1)) return false;
227 68251 : break;
228 110989 : case ASN1_CONTEXT(2):
229 110989 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false;
230 110989 : if (!asn1_read_OctetString(asn1, mem_ctx, &token->responseToken)) return false;
231 110989 : if (!asn1_end_tag(asn1)) return false;
232 109676 : break;
233 76987 : case ASN1_CONTEXT(3):
234 76987 : if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false;
235 76987 : if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechListMIC)) return false;
236 76987 : if (!asn1_end_tag(asn1)) return false;
237 76515 : break;
238 0 : default:
239 0 : asn1_set_error(asn1);
240 0 : break;
241 : }
242 : }
243 :
244 148951 : if (!asn1_end_tag(asn1)) return false;
245 148951 : if (!asn1_end_tag(asn1)) return false;
246 :
247 148951 : return !asn1_has_error(asn1);
248 : }
249 :
250 149179 : static bool write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
251 : {
252 149179 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
253 149179 : if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
254 :
255 149179 : if (token->negResult != SPNEGO_NONE_RESULT) {
256 107335 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
257 107335 : if (!asn1_write_enumerated(asn1, token->negResult)) return false;
258 107335 : if (!asn1_pop_tag(asn1)) return false;
259 : }
260 :
261 149179 : if (token->supportedMech) {
262 69454 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
263 69454 : if (!asn1_write_OID(asn1, token->supportedMech)) return false;
264 69454 : if (!asn1_pop_tag(asn1)) return false;
265 : }
266 :
267 149179 : if (token->responseToken.data) {
268 111286 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false;
269 111286 : if (!asn1_write_OctetString(asn1, token->responseToken.data,
270 0 : token->responseToken.length)) return false;
271 111286 : if (!asn1_pop_tag(asn1)) return false;
272 : }
273 :
274 149179 : if (token->mechListMIC.data) {
275 76974 : if (!asn1_push_tag(asn1, ASN1_CONTEXT(3))) return false;
276 76974 : if (!asn1_write_OctetString(asn1, token->mechListMIC.data,
277 0 : token->mechListMIC.length)) return false;
278 76974 : if (!asn1_pop_tag(asn1)) return false;
279 : }
280 :
281 149179 : if (!asn1_pop_tag(asn1)) return false;
282 149179 : if (!asn1_pop_tag(asn1)) return false;
283 :
284 149179 : return !asn1_has_error(asn1);
285 : }
286 :
287 257528 : ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data *token)
288 : {
289 3748 : struct asn1_data *asn1;
290 257528 : ssize_t ret = -1;
291 3748 : uint8_t context;
292 :
293 257528 : ZERO_STRUCTP(token);
294 :
295 257528 : if (data.length == 0) {
296 0 : return ret;
297 : }
298 :
299 257528 : asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
300 257528 : if (asn1 == NULL) {
301 0 : return -1;
302 : }
303 :
304 257528 : if (!asn1_load(asn1, data)) goto err;
305 :
306 257528 : if (!asn1_peek_uint8(asn1, &context)) {
307 0 : asn1_set_error(asn1);
308 : } else {
309 257528 : switch (context) {
310 108475 : case ASN1_APPLICATION(0):
311 108475 : if (!asn1_start_tag(asn1, ASN1_APPLICATION(0))) goto err;
312 108475 : if (!asn1_check_OID(asn1, OID_SPNEGO)) goto err;
313 108407 : if (read_negTokenInit(asn1, mem_ctx, &token->negTokenInit)) {
314 108407 : token->type = SPNEGO_NEG_TOKEN_INIT;
315 : }
316 108407 : if (!asn1_end_tag(asn1)) goto err;
317 106223 : break;
318 148951 : case ASN1_CONTEXT(1):
319 148951 : if (read_negTokenTarg(asn1, mem_ctx, &token->negTokenTarg)) {
320 148951 : token->type = SPNEGO_NEG_TOKEN_TARG;
321 : }
322 147387 : break;
323 102 : default:
324 102 : asn1_set_error(asn1);
325 102 : break;
326 : }
327 : }
328 :
329 257460 : if (!asn1_has_error(asn1)) {
330 257358 : ret = asn1_current_ofs(asn1);
331 : }
332 :
333 102 : err:
334 :
335 257528 : asn1_free(asn1);
336 :
337 257528 : return ret;
338 : }
339 :
340 272082 : ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
341 : {
342 272082 : struct asn1_data *asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
343 272082 : ssize_t ret = -1;
344 :
345 272082 : if (asn1 == NULL) {
346 0 : return -1;
347 : }
348 :
349 272082 : switch (spnego->type) {
350 122903 : case SPNEGO_NEG_TOKEN_INIT:
351 122903 : if (!asn1_push_tag(asn1, ASN1_APPLICATION(0))) goto err;
352 122903 : if (!asn1_write_OID(asn1, OID_SPNEGO)) goto err;
353 122903 : if (!write_negTokenInit(asn1, &spnego->negTokenInit)) goto err;
354 122903 : if (!asn1_pop_tag(asn1)) goto err;
355 120252 : break;
356 149179 : case SPNEGO_NEG_TOKEN_TARG:
357 149179 : write_negTokenTarg(asn1, &spnego->negTokenTarg);
358 149179 : break;
359 0 : default:
360 0 : asn1_set_error(asn1);
361 0 : break;
362 : }
363 :
364 272082 : if (!asn1_extract_blob(asn1, mem_ctx, blob)) {
365 0 : goto err;
366 : }
367 :
368 272082 : ret = asn1_current_ofs(asn1);
369 :
370 272082 : err:
371 :
372 272082 : asn1_free(asn1);
373 :
374 272082 : 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 192550 : bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
410 : const char * const *mech_types,
411 : DATA_BLOB *blob)
412 : {
413 192550 : bool ret = false;
414 192550 : struct asn1_data *asn1 = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
415 :
416 192550 : if (asn1 == NULL) {
417 0 : return false;
418 : }
419 :
420 : /* Write mechTypes */
421 192550 : if (mech_types && *mech_types) {
422 3704 : int i;
423 :
424 192550 : if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) goto err;
425 538856 : for (i = 0; mech_types[i]; i++) {
426 346306 : if (!asn1_write_OID(asn1, mech_types[i])) goto err;
427 : }
428 192550 : if (!asn1_pop_tag(asn1)) goto err;
429 : }
430 :
431 192550 : if (asn1_has_error(asn1)) {
432 0 : goto err;
433 : }
434 :
435 192550 : if (!asn1_extract_blob(asn1, mem_ctx, blob)) {
436 0 : goto err;
437 : }
438 :
439 188846 : ret = true;
440 :
441 192550 : err:
442 :
443 192550 : asn1_free(asn1);
444 :
445 192550 : return ret;
446 : }
|