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