idmap: Fix whitespace
[obnox/samba/samba-obnox.git] / libcli / auth / spnego_parse.c
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 static bool read_negTokenInit(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
28                               struct spnego_negTokenInit *token)
29 {
30         ZERO_STRUCTP(token);
31
32         if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
33         if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
34
35         while (!asn1_has_error(asn1) && 0 < asn1_tag_remaining(asn1)) {
36                 int i;
37                 uint8_t context;
38
39                 if (!asn1_peek_uint8(asn1, &context)) {
40                         asn1_set_error(asn1);
41                         break;
42                 }
43
44                 switch (context) {
45                 /* Read mechTypes */
46                 case ASN1_CONTEXT(0): {
47                         const char **mechTypes;
48
49                         if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
50                         if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
51
52                         mechTypes = talloc(mem_ctx, const char *);
53                         if (mechTypes == NULL) {
54                                 asn1_set_error(asn1);
55                                 return false;
56                         }
57                         for (i = 0; !asn1_has_error(asn1) &&
58                                      0 < asn1_tag_remaining(asn1); i++) {
59                                 char *oid;
60                                 const char **p;
61                                 p = talloc_realloc(mem_ctx,
62                                                    mechTypes,
63                                                    const char *, i+2);
64                                 if (p == NULL) {
65                                         talloc_free(mechTypes);
66                                         asn1_set_error(asn1);
67                                         return false;
68                                 }
69                                 mechTypes = p;
70
71                                 if (!asn1_read_OID(asn1, mechTypes, &oid)) return false;
72                                 mechTypes[i] = oid;
73                         }
74                         mechTypes[i] = NULL;
75                         token->mechTypes = mechTypes;
76
77                         asn1_end_tag(asn1);
78                         asn1_end_tag(asn1);
79                         break;
80                 }
81                 /* Read reqFlags */
82                 case ASN1_CONTEXT(1):
83                         if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
84                         if (!asn1_read_BitString(asn1, mem_ctx, &token->reqFlags,
85                                             &token->reqFlagsPadding)) return false;
86                         if (!asn1_end_tag(asn1)) return false;
87                         break;
88                 /* Read mechToken */
89                 case ASN1_CONTEXT(2):
90                         if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false;
91                         if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechToken)) return false;
92                         if (!asn1_end_tag(asn1)) return false;
93                         break;
94                 /* Read mecListMIC */
95                 case ASN1_CONTEXT(3):
96                 {
97                         uint8_t type_peek;
98                         if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false;
99                         if (!asn1_peek_uint8(asn1, &type_peek)) {
100                                 asn1_set_error(asn1);
101                                 break;
102                         }
103                         if (type_peek == ASN1_OCTET_STRING) {
104                                 if (!asn1_read_OctetString(asn1, mem_ctx,
105                                                       &token->mechListMIC)) return false;
106                         } else {
107                                 /* RFC 2478 says we have an Octet String here,
108                                    but W2k sends something different... */
109                                 char *mechListMIC;
110                                 if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
111                                 if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
112                                 if (!asn1_read_GeneralString(asn1, mem_ctx, &mechListMIC)) return false;
113                                 if (!asn1_end_tag(asn1)) return false;
114                                 if (!asn1_end_tag(asn1)) return false;
115
116                                 token->targetPrincipal = mechListMIC;
117                         }
118                         if (!asn1_end_tag(asn1)) return false;
119                         break;
120                 }
121                 default:
122                         asn1_set_error(asn1);
123                         break;
124                 }
125         }
126
127         if (!asn1_end_tag(asn1)) return false;
128         if (!asn1_end_tag(asn1)) return false;
129
130         return !asn1_has_error(asn1);
131 }
132
133 static bool write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
134 {
135         if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
136         if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
137
138         /* Write mechTypes */
139         if (token->mechTypes && *token->mechTypes) {
140                 int i;
141
142                 if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
143                 if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
144                 for (i = 0; token->mechTypes[i]; i++) {
145                         if (!asn1_write_OID(asn1, token->mechTypes[i])) return false;
146                 }
147                 if (!asn1_pop_tag(asn1)) return false;
148                 if (!asn1_pop_tag(asn1)) return false;
149         }
150
151         /* write reqFlags */
152         if (token->reqFlags.length > 0) {
153                 if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
154                 if (!asn1_write_BitString(asn1, token->reqFlags.data,
155                                      token->reqFlags.length,
156                                      token->reqFlagsPadding)) return false;
157                 if (!asn1_pop_tag(asn1)) return false;
158         }
159
160         /* write mechToken */
161         if (token->mechToken.data) {
162                 if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false;
163                 if (!asn1_write_OctetString(asn1, token->mechToken.data,
164                                        token->mechToken.length)) return false;
165                 if (!asn1_pop_tag(asn1)) return false;
166         }
167
168         /* write mechListMIC */
169         if (token->mechListMIC.data) {
170                 if (!asn1_push_tag(asn1, ASN1_CONTEXT(3))) return false;
171 #if 0
172                 /* This is what RFC 2478 says ... */
173                 asn1_write_OctetString(asn1, token->mechListMIC.data,
174                                        token->mechListMIC.length);
175 #else
176                 /* ... but unfortunately this is what Windows
177                    sends/expects */
178                 if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
179                 if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
180                 if (!asn1_push_tag(asn1, ASN1_GENERAL_STRING)) return false;
181                 if (!asn1_write(asn1, token->mechListMIC.data,
182                            token->mechListMIC.length)) return false;
183                 if (!asn1_pop_tag(asn1)) return false;
184                 if (!asn1_pop_tag(asn1)) return false;
185                 if (!asn1_pop_tag(asn1)) return false;
186 #endif
187                 if (!asn1_pop_tag(asn1)) return false;
188         }
189
190         if (!asn1_pop_tag(asn1)) return false;
191         if (!asn1_pop_tag(asn1)) return false;
192
193         return !asn1_has_error(asn1);
194 }
195
196 static bool read_negTokenTarg(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
197                               struct spnego_negTokenTarg *token)
198 {
199         ZERO_STRUCTP(token);
200
201         if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
202         if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
203
204         while (!asn1_has_error(asn1) && 0 < asn1_tag_remaining(asn1)) {
205                 uint8_t context;
206                 char *oid;
207                 if (!asn1_peek_uint8(asn1, &context)) {
208                         asn1_set_error(asn1);
209                         break;
210                 }
211
212                 switch (context) {
213                 case ASN1_CONTEXT(0):
214                         if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
215                         if (!asn1_start_tag(asn1, ASN1_ENUMERATED)) return false;
216                         if (!asn1_read_uint8(asn1, &token->negResult)) return false;
217                         if (!asn1_end_tag(asn1)) return false;
218                         if (!asn1_end_tag(asn1)) return false;
219                         break;
220                 case ASN1_CONTEXT(1):
221                         if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
222                         if (!asn1_read_OID(asn1, mem_ctx, &oid)) return false;
223                         token->supportedMech = oid;
224                         if (!asn1_end_tag(asn1)) return false;
225                         break;
226                 case ASN1_CONTEXT(2):
227                         if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false;
228                         if (!asn1_read_OctetString(asn1, mem_ctx, &token->responseToken)) return false;
229                         if (!asn1_end_tag(asn1)) return false;
230                         break;
231                 case ASN1_CONTEXT(3):
232                         if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false;
233                         if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechListMIC)) return false;
234                         if (!asn1_end_tag(asn1)) return false;
235                         break;
236                 default:
237                         asn1_set_error(asn1);
238                         break;
239                 }
240         }
241
242         if (!asn1_end_tag(asn1)) return false;
243         if (!asn1_end_tag(asn1)) return false;
244
245         return !asn1_has_error(asn1);
246 }
247
248 static bool write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
249 {
250         if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
251         if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
252
253         if (token->negResult != SPNEGO_NONE_RESULT) {
254                 if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
255                 if (!asn1_write_enumerated(asn1, token->negResult)) return false;
256                 if (!asn1_pop_tag(asn1)) return false;
257         }
258
259         if (token->supportedMech) {
260                 if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
261                 if (!asn1_write_OID(asn1, token->supportedMech)) return false;
262                 if (!asn1_pop_tag(asn1)) return false;
263         }
264
265         if (token->responseToken.data) {
266                 if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false;
267                 if (!asn1_write_OctetString(asn1, token->responseToken.data,
268                                        token->responseToken.length)) return false;
269                 if (!asn1_pop_tag(asn1)) return false;
270         }
271
272         if (token->mechListMIC.data) {
273                 if (!asn1_push_tag(asn1, ASN1_CONTEXT(3))) return false;
274                 if (!asn1_write_OctetString(asn1, token->mechListMIC.data,
275                                       token->mechListMIC.length)) return false;
276                 if (!asn1_pop_tag(asn1)) return false;
277         }
278
279         if (!asn1_pop_tag(asn1)) return false;
280         if (!asn1_pop_tag(asn1)) return false;
281
282         return !asn1_has_error(asn1);
283 }
284
285 ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data *token)
286 {
287         struct asn1_data *asn1;
288         ssize_t ret = -1;
289         uint8_t context;
290
291         ZERO_STRUCTP(token);
292
293         if (data.length == 0) {
294                 return ret;
295         }
296
297         asn1 = asn1_init(mem_ctx);
298         if (asn1 == NULL) {
299                 return -1;
300         }
301
302         if (!asn1_load(asn1, data)) goto err;
303
304         if (!asn1_peek_uint8(asn1, &context)) {
305                 asn1_set_error(asn1);
306         } else {
307                 switch (context) {
308                 case ASN1_APPLICATION(0):
309                         if (!asn1_start_tag(asn1, ASN1_APPLICATION(0))) goto err;
310                         if (!asn1_check_OID(asn1, OID_SPNEGO)) goto err;
311                         if (read_negTokenInit(asn1, mem_ctx, &token->negTokenInit)) {
312                                 token->type = SPNEGO_NEG_TOKEN_INIT;
313                         }
314                         if (!asn1_end_tag(asn1)) goto err;
315                         break;
316                 case ASN1_CONTEXT(1):
317                         if (read_negTokenTarg(asn1, mem_ctx, &token->negTokenTarg)) {
318                                 token->type = SPNEGO_NEG_TOKEN_TARG;
319                         }
320                         break;
321                 default:
322                         asn1_set_error(asn1);
323                         break;
324                 }
325         }
326
327         if (!asn1_has_error(asn1)) {
328                 ret = asn1_current_ofs(asn1);
329         }
330
331   err:
332
333         asn1_free(asn1);
334
335         return ret;
336 }
337
338 ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
339 {
340         struct asn1_data *asn1 = asn1_init(mem_ctx);
341         ssize_t ret = -1;
342
343         if (asn1 == NULL) {
344                 return -1;
345         }
346
347         switch (spnego->type) {
348         case SPNEGO_NEG_TOKEN_INIT:
349                 if (!asn1_push_tag(asn1, ASN1_APPLICATION(0))) goto err;
350                 if (!asn1_write_OID(asn1, OID_SPNEGO)) goto err;
351                 if (!write_negTokenInit(asn1, &spnego->negTokenInit)) goto err;
352                 if (!asn1_pop_tag(asn1)) goto err;
353                 break;
354         case SPNEGO_NEG_TOKEN_TARG:
355                 write_negTokenTarg(asn1, &spnego->negTokenTarg);
356                 break;
357         default:
358                 asn1_set_error(asn1);
359                 break;
360         }
361
362         if (!asn1_extract_blob(asn1, mem_ctx, blob)) {
363                 goto err;
364         }
365
366         ret = asn1_current_ofs(asn1);
367
368   err:
369
370         asn1_free(asn1);
371
372         return ret;
373 }
374
375 bool spnego_free_data(struct spnego_data *spnego)
376 {
377         bool ret = true;
378
379         if (!spnego) goto out;
380
381         switch(spnego->type) {
382         case SPNEGO_NEG_TOKEN_INIT:
383                 if (spnego->negTokenInit.mechTypes) {
384                         talloc_free(discard_const(spnego->negTokenInit.mechTypes));
385                 }
386                 data_blob_free(&spnego->negTokenInit.reqFlags);
387                 data_blob_free(&spnego->negTokenInit.mechToken);
388                 data_blob_free(&spnego->negTokenInit.mechListMIC);
389                 talloc_free(spnego->negTokenInit.targetPrincipal);
390                 break;
391         case SPNEGO_NEG_TOKEN_TARG:
392                 if (spnego->negTokenTarg.supportedMech) {
393                         talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
394                 }
395                 data_blob_free(&spnego->negTokenTarg.responseToken);
396                 data_blob_free(&spnego->negTokenTarg.mechListMIC);
397                 break;
398         default:
399                 ret = false;
400                 break;
401         }
402         ZERO_STRUCTP(spnego);
403 out:
404         return ret;
405 }
406
407 bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
408                              const char * const *mech_types,
409                              DATA_BLOB *blob)
410 {
411         bool ret = false;
412         struct asn1_data *asn1 = asn1_init(mem_ctx);
413
414         if (asn1 == NULL) {
415                 return false;
416         }
417
418         /* Write mechTypes */
419         if (mech_types && *mech_types) {
420                 int i;
421
422                 if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) goto err;
423                 for (i = 0; mech_types[i]; i++) {
424                         if (!asn1_write_OID(asn1, mech_types[i])) goto err;
425                 }
426                 if (!asn1_pop_tag(asn1)) goto err;
427         }
428
429         if (asn1_has_error(asn1)) {
430                 goto err;
431         }
432
433         if (!asn1_extract_blob(asn1, mem_ctx, blob)) {
434                 goto err;
435         }
436
437         ret = true;
438
439   err:
440
441         asn1_free(asn1);
442
443         return ret;
444 }