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