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