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