r1438: Record the principal name we are sent in the SPENGO mechListMIC in a
[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 = malloc(sizeof(*token->mechTypes));
51                         for (i = 0; !asn1->has_error &&
52                                      0 < asn1_tag_remaining(asn1); i++) {
53                                 token->mechTypes = 
54                                         realloc(token->mechTypes, (i + 2) *
55                                                 sizeof(*token->mechTypes));
56                                 asn1_read_OID(asn1, 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, &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,
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, &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(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(ASN1_DATA *asn1, struct spnego_negTokenTarg *token)
179 {
180         ZERO_STRUCTP(token);
181
182         asn1_start_tag(asn1, ASN1_CONTEXT(1));
183         asn1_start_tag(asn1, ASN1_SEQUENCE(0));
184
185         while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
186                 uint8_t context;
187                 if (!asn1_peek_uint8(asn1, &context)) {
188                         asn1->has_error = True;
189                         break;
190                 }
191
192                 switch (context) {
193                 case ASN1_CONTEXT(0):
194                         asn1_start_tag(asn1, ASN1_CONTEXT(0));
195                         asn1_start_tag(asn1, ASN1_ENUMERATED);
196                         asn1_read_uint8(asn1, &token->negResult);
197                         asn1_end_tag(asn1);
198                         asn1_end_tag(asn1);
199                         break;
200                 case ASN1_CONTEXT(1):
201                         asn1_start_tag(asn1, ASN1_CONTEXT(1));
202                         asn1_read_OID(asn1, &token->supportedMech);
203                         asn1_end_tag(asn1);
204                         break;
205                 case ASN1_CONTEXT(2):
206                         asn1_start_tag(asn1, ASN1_CONTEXT(2));
207                         asn1_read_OctetString(asn1, &token->responseToken);
208                         asn1_end_tag(asn1);
209                         break;
210                 case ASN1_CONTEXT(3):
211                         asn1_start_tag(asn1, ASN1_CONTEXT(3));
212                         asn1_read_OctetString(asn1, &token->mechListMIC);
213                         asn1_end_tag(asn1);
214                         break;
215                 default:
216                         asn1->has_error = True;
217                         break;
218                 }
219         }
220
221         asn1_end_tag(asn1);
222         asn1_end_tag(asn1);
223
224         return !asn1->has_error;
225 }
226
227 static BOOL write_negTokenTarg(ASN1_DATA *asn1, struct spnego_negTokenTarg *token)
228 {
229         asn1_push_tag(asn1, ASN1_CONTEXT(1));
230         asn1_push_tag(asn1, ASN1_SEQUENCE(0));
231
232         if (token->negResult != SPNEGO_NONE_RESULT) {
233                 asn1_push_tag(asn1, ASN1_CONTEXT(0));
234                 asn1_write_enumerated(asn1, token->negResult);
235                 asn1_pop_tag(asn1);
236         }
237
238         if (token->supportedMech) {
239                 asn1_push_tag(asn1, ASN1_CONTEXT(1));
240                 asn1_write_OID(asn1, token->supportedMech);
241                 asn1_pop_tag(asn1);
242         }
243
244         if (token->responseToken.data) {
245                 asn1_push_tag(asn1, ASN1_CONTEXT(2));
246                 asn1_write_OctetString(asn1, token->responseToken.data,
247                                        token->responseToken.length);
248                 asn1_pop_tag(asn1);
249         }
250
251         if (token->mechListMIC.data) {
252                 asn1_push_tag(asn1, ASN1_CONTEXT(3));
253                 asn1_write_OctetString(asn1, token->mechListMIC.data,
254                                       token->mechListMIC.length);
255                 asn1_pop_tag(asn1);
256         }
257
258         asn1_pop_tag(asn1);
259         asn1_pop_tag(asn1);
260
261         return !asn1->has_error;
262 }
263
264 ssize_t spnego_read_data(DATA_BLOB data, struct spnego_data *token)
265 {
266         ASN1_DATA asn1;
267         ssize_t ret = -1;
268         uint8_t context;
269
270         ZERO_STRUCTP(token);
271         ZERO_STRUCT(asn1);
272
273         if (data.length == 0) {
274                 return ret;
275         }
276
277         asn1_load(&asn1, data);
278
279         if (!asn1_peek_uint8(&asn1, &context)) {
280                 asn1.has_error = True;
281         } else {
282                 switch (context) {
283                 case ASN1_APPLICATION(0):
284                         asn1_start_tag(&asn1, ASN1_APPLICATION(0));
285                         asn1_check_OID(&asn1, OID_SPNEGO);
286                         if (read_negTokenInit(&asn1, &token->negTokenInit)) {
287                                 token->type = SPNEGO_NEG_TOKEN_INIT;
288                         }
289                         asn1_end_tag(&asn1);
290                         break;
291                 case ASN1_CONTEXT(1):
292                         if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
293                                 token->type = SPNEGO_NEG_TOKEN_TARG;
294                         }
295                         break;
296                 default:
297                         asn1.has_error = True;
298                         break;
299                 }
300         }
301
302         if (!asn1.has_error) ret = asn1.ofs;
303         asn1_free(&asn1);
304
305         return ret;
306 }
307
308 ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
309 {
310         ASN1_DATA asn1;
311         ssize_t ret = -1;
312
313         ZERO_STRUCT(asn1);
314
315         switch (spnego->type) {
316         case SPNEGO_NEG_TOKEN_INIT:
317                 asn1_push_tag(&asn1, ASN1_APPLICATION(0));
318                 asn1_write_OID(&asn1, OID_SPNEGO);
319                 write_negTokenInit(&asn1, &spnego->negTokenInit);
320                 asn1_pop_tag(&asn1);
321                 break;
322         case SPNEGO_NEG_TOKEN_TARG:
323                 write_negTokenTarg(&asn1, &spnego->negTokenTarg);
324                 break;
325         default:
326                 asn1.has_error = True;
327                 break;
328         }
329
330         if (!asn1.has_error) {
331                 *blob = data_blob_talloc(mem_ctx, asn1.data, asn1.length);
332                 ret = asn1.ofs;
333         }
334         asn1_free(&asn1);
335
336         return ret;
337 }
338
339 BOOL spnego_free_data(struct spnego_data *spnego)
340 {
341         BOOL ret = True;
342
343         if (!spnego) goto out;
344
345         switch(spnego->type) {
346         case SPNEGO_NEG_TOKEN_INIT:
347                 if (spnego->negTokenInit.mechTypes) {
348                         int i;
349                         for (i = 0; spnego->negTokenInit.mechTypes[i]; i++) {
350                                 free(spnego->negTokenInit.mechTypes[i]);
351                         }
352                         free(spnego->negTokenInit.mechTypes);
353                 }
354                 data_blob_free(&spnego->negTokenInit.mechToken);
355                 data_blob_free(&spnego->negTokenInit.mechListMIC);
356                 SAFE_FREE(spnego->negTokenInit.targetPrincipal);
357                 break;
358         case SPNEGO_NEG_TOKEN_TARG:
359                 if (spnego->negTokenTarg.supportedMech) {
360                         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