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