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