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