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