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