r3323: more warning reductions
[bbaumbach/samba-autobuild/.git] / source4 / 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 2 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, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_AUTH
28
29 static BOOL read_negTokenInit(ASN1_DATA *asn1, struct spnego_negTokenInit *token)
30 {
31         ZERO_STRUCTP(token);
32
33         asn1_start_tag(asn1, ASN1_CONTEXT(0));
34         asn1_start_tag(asn1, ASN1_SEQUENCE(0));
35
36         while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
37                 int i;
38                 uint8_t context;
39                 if (!asn1_peek_uint8(asn1, &context)) {
40                         asn1->has_error = True;
41                         break;
42                 }
43
44                 switch (context) {
45                 /* Read mechTypes */
46                 case ASN1_CONTEXT(0):
47                         asn1_start_tag(asn1, ASN1_CONTEXT(0));
48                         asn1_start_tag(asn1, ASN1_SEQUENCE(0));
49
50                         token->mechTypes = talloc(NULL, sizeof(*token->mechTypes));
51                         for (i = 0; !asn1->has_error &&
52                                      0 < asn1_tag_remaining(asn1); i++) {
53                                 token->mechTypes = 
54                                         talloc_realloc(NULL, token->mechTypes, (i + 2) *
55                                                        sizeof(*token->mechTypes));
56                                 asn1_read_OID(asn1, token->mechTypes + i);
57                                 if (token->mechTypes[i]) {
58                                         talloc_steal(token->mechTypes, 
59                                                      token->mechTypes[i]);
60                                 }
61                         }
62                         token->mechTypes[i] = NULL;
63                         
64                         asn1_end_tag(asn1);
65                         asn1_end_tag(asn1);
66                         break;
67                 /* Read reqFlags */
68                 case ASN1_CONTEXT(1):
69                         asn1_start_tag(asn1, ASN1_CONTEXT(1));
70                         asn1_read_Integer(asn1, &token->reqFlags);
71                         token->reqFlags |= SPNEGO_REQ_FLAG;
72                         asn1_end_tag(asn1);
73                         break;
74                 /* Read mechToken */
75                 case ASN1_CONTEXT(2):
76                         asn1_start_tag(asn1, ASN1_CONTEXT(2));
77                         asn1_read_OctetString(asn1, &token->mechToken);
78                         asn1_end_tag(asn1);
79                         break;
80                 /* Read mecListMIC */
81                 case ASN1_CONTEXT(3):
82                 {
83                         uint8_t type_peek;
84                         asn1_start_tag(asn1, ASN1_CONTEXT(3));
85                         if (!asn1_peek_uint8(asn1, &type_peek)) {
86                                 asn1->has_error = True;
87                                 break;
88                         }
89                         if (type_peek == ASN1_OCTET_STRING) {
90                                 asn1_read_OctetString(asn1,
91                                                       &token->mechListMIC);
92                         } else {
93                                 /* RFC 2478 says we have an Octet String here,
94                                    but W2k sends something different... */
95                                 char *mechListMIC;
96                                 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
97                                 asn1_push_tag(asn1, ASN1_CONTEXT(0));
98                                 asn1_read_GeneralString(asn1, &mechListMIC);
99                                 asn1_pop_tag(asn1);
100                                 asn1_pop_tag(asn1);
101
102                                 token->targetPrincipal = mechListMIC;
103                         }
104                         asn1_end_tag(asn1);
105                         break;
106                 }
107                 default:
108                         asn1->has_error = True;
109                         break;
110                 }
111         }
112
113         asn1_end_tag(asn1);
114         asn1_end_tag(asn1);
115
116         return !asn1->has_error;
117 }
118
119 static BOOL write_negTokenInit(ASN1_DATA *asn1, struct spnego_negTokenInit *token)
120 {
121         asn1_push_tag(asn1, ASN1_CONTEXT(0));
122         asn1_push_tag(asn1, ASN1_SEQUENCE(0));
123
124         /* Write mechTypes */
125         if (token->mechTypes && *token->mechTypes) {
126                 int i;
127
128                 asn1_push_tag(asn1, ASN1_CONTEXT(0));
129                 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
130                 for (i = 0; token->mechTypes[i]; i++) {
131                         asn1_write_OID(asn1, token->mechTypes[i]);
132                 }
133                 asn1_pop_tag(asn1);
134                 asn1_pop_tag(asn1);
135         }
136
137         /* write reqFlags */
138         if (token->reqFlags & SPNEGO_REQ_FLAG) {
139                 int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
140
141                 asn1_push_tag(asn1, ASN1_CONTEXT(1));
142                 asn1_write_Integer(asn1, flags);
143                 asn1_pop_tag(asn1);
144         }
145
146         /* write mechToken */
147         if (token->mechToken.data) {
148                 asn1_push_tag(asn1, ASN1_CONTEXT(2));
149                 asn1_write_OctetString(asn1, token->mechToken.data,
150                                        token->mechToken.length);
151                 asn1_pop_tag(asn1);
152         }
153
154         /* write mechListMIC */
155         if (token->mechListMIC.data) {
156                 asn1_push_tag(asn1, ASN1_CONTEXT(3));
157 #if 0
158                 /* This is what RFC 2478 says ... */
159                 asn1_write_OctetString(asn1, token->mechListMIC.data,
160                                        token->mechListMIC.length);
161 #else
162                 /* ... but unfortunately this is what Windows
163                    sends/expects */
164                 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
165                 asn1_push_tag(asn1, ASN1_CONTEXT(0));
166                 asn1_push_tag(asn1, ASN1_GENERAL_STRING);
167                 asn1_write(asn1, token->mechListMIC.data,
168                            token->mechListMIC.length);
169                 asn1_pop_tag(asn1);
170                 asn1_pop_tag(asn1);
171                 asn1_pop_tag(asn1);
172 #endif          
173                 asn1_pop_tag(asn1);
174         }
175
176         asn1_pop_tag(asn1);
177         asn1_pop_tag(asn1);
178
179         return !asn1->has_error;
180 }
181
182 static BOOL read_negTokenTarg(ASN1_DATA *asn1, struct spnego_negTokenTarg *token)
183 {
184         ZERO_STRUCTP(token);
185
186         asn1_start_tag(asn1, ASN1_CONTEXT(1));
187         asn1_start_tag(asn1, ASN1_SEQUENCE(0));
188
189         while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
190                 uint8_t context;
191                 if (!asn1_peek_uint8(asn1, &context)) {
192                         asn1->has_error = True;
193                         break;
194                 }
195
196                 switch (context) {
197                 case ASN1_CONTEXT(0):
198                         asn1_start_tag(asn1, ASN1_CONTEXT(0));
199                         asn1_start_tag(asn1, ASN1_ENUMERATED);
200                         asn1_read_uint8(asn1, &token->negResult);
201                         asn1_end_tag(asn1);
202                         asn1_end_tag(asn1);
203                         break;
204                 case ASN1_CONTEXT(1):
205                         asn1_start_tag(asn1, ASN1_CONTEXT(1));
206                         asn1_read_OID(asn1, &token->supportedMech);
207                         asn1_end_tag(asn1);
208                         break;
209                 case ASN1_CONTEXT(2):
210                         asn1_start_tag(asn1, ASN1_CONTEXT(2));
211                         asn1_read_OctetString(asn1, &token->responseToken);
212                         asn1_end_tag(asn1);
213                         break;
214                 case ASN1_CONTEXT(3):
215                         asn1_start_tag(asn1, ASN1_CONTEXT(3));
216                         asn1_read_OctetString(asn1, &token->mechListMIC);
217                         asn1_end_tag(asn1);
218                         break;
219                 default:
220                         asn1->has_error = True;
221                         break;
222                 }
223         }
224
225         asn1_end_tag(asn1);
226         asn1_end_tag(asn1);
227
228         return !asn1->has_error;
229 }
230
231 static BOOL write_negTokenTarg(ASN1_DATA *asn1, struct spnego_negTokenTarg *token)
232 {
233         asn1_push_tag(asn1, ASN1_CONTEXT(1));
234         asn1_push_tag(asn1, ASN1_SEQUENCE(0));
235
236         if (token->negResult != SPNEGO_NONE_RESULT) {
237                 asn1_push_tag(asn1, ASN1_CONTEXT(0));
238                 asn1_write_enumerated(asn1, token->negResult);
239                 asn1_pop_tag(asn1);
240         }
241
242         if (token->supportedMech) {
243                 asn1_push_tag(asn1, ASN1_CONTEXT(1));
244                 asn1_write_OID(asn1, token->supportedMech);
245                 asn1_pop_tag(asn1);
246         }
247
248         if (token->responseToken.data) {
249                 asn1_push_tag(asn1, ASN1_CONTEXT(2));
250                 asn1_write_OctetString(asn1, token->responseToken.data,
251                                        token->responseToken.length);
252                 asn1_pop_tag(asn1);
253         }
254
255         if (token->mechListMIC.data) {
256                 asn1_push_tag(asn1, ASN1_CONTEXT(3));
257                 asn1_write_OctetString(asn1, token->mechListMIC.data,
258                                       token->mechListMIC.length);
259                 asn1_pop_tag(asn1);
260         }
261
262         asn1_pop_tag(asn1);
263         asn1_pop_tag(asn1);
264
265         return !asn1->has_error;
266 }
267
268 ssize_t spnego_read_data(DATA_BLOB data, struct spnego_data *token)
269 {
270         ASN1_DATA asn1;
271         ssize_t ret = -1;
272         uint8_t context;
273
274         ZERO_STRUCTP(token);
275         ZERO_STRUCT(asn1);
276
277         if (data.length == 0) {
278                 return ret;
279         }
280
281         asn1_load(&asn1, data);
282
283         if (!asn1_peek_uint8(&asn1, &context)) {
284                 asn1.has_error = True;
285         } else {
286                 switch (context) {
287                 case ASN1_APPLICATION(0):
288                         asn1_start_tag(&asn1, ASN1_APPLICATION(0));
289                         asn1_check_OID(&asn1, OID_SPNEGO);
290                         if (read_negTokenInit(&asn1, &token->negTokenInit)) {
291                                 token->type = SPNEGO_NEG_TOKEN_INIT;
292                         }
293                         asn1_end_tag(&asn1);
294                         break;
295                 case ASN1_CONTEXT(1):
296                         if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
297                                 token->type = SPNEGO_NEG_TOKEN_TARG;
298                         }
299                         break;
300                 default:
301                         asn1.has_error = True;
302                         break;
303                 }
304         }
305
306         if (!asn1.has_error) ret = asn1.ofs;
307         asn1_free(&asn1);
308
309         return ret;
310 }
311
312 ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
313 {
314         ASN1_DATA asn1;
315         ssize_t ret = -1;
316
317         ZERO_STRUCT(asn1);
318
319         switch (spnego->type) {
320         case SPNEGO_NEG_TOKEN_INIT:
321                 asn1_push_tag(&asn1, ASN1_APPLICATION(0));
322                 asn1_write_OID(&asn1, OID_SPNEGO);
323                 write_negTokenInit(&asn1, &spnego->negTokenInit);
324                 asn1_pop_tag(&asn1);
325                 break;
326         case SPNEGO_NEG_TOKEN_TARG:
327                 write_negTokenTarg(&asn1, &spnego->negTokenTarg);
328                 break;
329         default:
330                 asn1.has_error = True;
331                 break;
332         }
333
334         if (!asn1.has_error) {
335                 *blob = data_blob_talloc(mem_ctx, asn1.data, asn1.length);
336                 ret = asn1.ofs;
337         }
338         asn1_free(&asn1);
339
340         return ret;
341 }
342
343 BOOL spnego_free_data(struct spnego_data *spnego)
344 {
345         BOOL ret = True;
346
347         if (!spnego) goto out;
348
349         switch(spnego->type) {
350         case SPNEGO_NEG_TOKEN_INIT:
351                 if (spnego->negTokenInit.mechTypes) {
352                         talloc_free(spnego->negTokenInit.mechTypes);
353                 }
354                 data_blob_free(&spnego->negTokenInit.mechToken);
355                 data_blob_free(&spnego->negTokenInit.mechListMIC);
356                 talloc_free(spnego->negTokenInit.targetPrincipal);
357                 break;
358         case SPNEGO_NEG_TOKEN_TARG:
359                 if (spnego->negTokenTarg.supportedMech) {
360                         talloc_free(spnego->negTokenTarg.supportedMech);
361                 }
362                 data_blob_free(&spnego->negTokenTarg.responseToken);
363                 data_blob_free(&spnego->negTokenTarg.mechListMIC);
364                 break;
365         default:
366                 ret = False;
367                 break;
368         }
369         ZERO_STRUCTP(spnego);
370 out:
371         return ret;
372 }
373