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