s3: use shared asn1 code.
[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(NULL, 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(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                                 TALLO_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                         asn1_start_tag(asn1, ASN1_CONTEXT(1));
190                         asn1_read_OID(asn1, NULL, &token->supportedMech);
191                         asn1_end_tag(asn1);
192                         break;
193                 case ASN1_CONTEXT(2):
194                         asn1_start_tag(asn1, ASN1_CONTEXT(2));
195                         asn1_read_OctetString(asn1, NULL, &token->responseToken);
196                         asn1_end_tag(asn1);
197                         break;
198                 case ASN1_CONTEXT(3):
199                         asn1_start_tag(asn1, ASN1_CONTEXT(3));
200                         asn1_read_OctetString(asn1, NULL, &token->mechListMIC);
201                         asn1_end_tag(asn1);
202                         break;
203                 default:
204                         asn1->has_error = True;
205                         break;
206                 }
207         }
208
209         asn1_end_tag(asn1);
210         asn1_end_tag(asn1);
211
212         return !asn1->has_error;
213 }
214
215 static bool write_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
216 {
217         asn1_push_tag(asn1, ASN1_CONTEXT(1));
218         asn1_push_tag(asn1, ASN1_SEQUENCE(0));
219
220         asn1_push_tag(asn1, ASN1_CONTEXT(0));
221         asn1_write_enumerated(asn1, token->negResult);
222         asn1_pop_tag(asn1);
223
224         if (token->supportedMech) {
225                 asn1_push_tag(asn1, ASN1_CONTEXT(1));
226                 asn1_write_OID(asn1, token->supportedMech);
227                 asn1_pop_tag(asn1);
228         }
229
230         if (token->responseToken.data) {
231                 asn1_push_tag(asn1, ASN1_CONTEXT(2));
232                 asn1_write_OctetString(asn1, token->responseToken.data,
233                                        token->responseToken.length);
234                 asn1_pop_tag(asn1);
235         }
236
237         if (token->mechListMIC.data) {
238                 asn1_push_tag(asn1, ASN1_CONTEXT(3));
239                 asn1_write_OctetString(asn1, token->mechListMIC.data,
240                                       token->mechListMIC.length);
241                 asn1_pop_tag(asn1);
242         }
243
244         asn1_pop_tag(asn1);
245         asn1_pop_tag(asn1);
246
247         return !asn1->has_error;
248 }
249
250 ssize_t read_spnego_data(DATA_BLOB data, SPNEGO_DATA *token)
251 {
252         ASN1_DATA asn1;
253         ssize_t ret = -1;
254
255         ZERO_STRUCTP(token);
256         ZERO_STRUCT(asn1);
257         asn1_load(&asn1, data);
258
259         switch (asn1.data[asn1.ofs]) {
260         case ASN1_APPLICATION(0):
261                 asn1_start_tag(&asn1, ASN1_APPLICATION(0));
262                 asn1_check_OID(&asn1, OID_SPNEGO);
263                 if (read_negTokenInit(&asn1, &token->negTokenInit)) {
264                         token->type = SPNEGO_NEG_TOKEN_INIT;
265                 }
266                 asn1_end_tag(&asn1);
267                 break;
268         case ASN1_CONTEXT(1):
269                 if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
270                         token->type = SPNEGO_NEG_TOKEN_TARG;
271                 }
272                 break;
273         default:
274                 break;
275         }
276
277         if (!asn1.has_error) ret = asn1.ofs;
278         asn1_free(&asn1);
279
280         return ret;
281 }
282
283 ssize_t write_spnego_data(DATA_BLOB *blob, SPNEGO_DATA *spnego)
284 {
285         ASN1_DATA asn1;
286         ssize_t ret = -1;
287
288         ZERO_STRUCT(asn1);
289
290         switch (spnego->type) {
291         case SPNEGO_NEG_TOKEN_INIT:
292                 asn1_push_tag(&asn1, ASN1_APPLICATION(0));
293                 asn1_write_OID(&asn1, OID_SPNEGO);
294                 write_negTokenInit(&asn1, &spnego->negTokenInit);
295                 asn1_pop_tag(&asn1);
296                 break;
297         case SPNEGO_NEG_TOKEN_TARG:
298                 write_negTokenTarg(&asn1, &spnego->negTokenTarg);
299                 break;
300         default:
301                 asn1.has_error = True;
302                 break;
303         }
304
305         if (!asn1.has_error) {
306                 *blob = data_blob(asn1.data, asn1.length);
307                 ret = asn1.ofs;
308         }
309         asn1_free(&asn1);
310
311         return ret;
312 }
313
314 bool free_spnego_data(SPNEGO_DATA *spnego)
315 {
316         bool ret = True;
317
318         if (!spnego) goto out;
319
320         switch(spnego->type) {
321         case SPNEGO_NEG_TOKEN_INIT:
322                 if (spnego->negTokenInit.mechTypes) {
323                         int i;
324                         for (i = 0; spnego->negTokenInit.mechTypes[i]; i++) {
325                                 talloc_free(CONST_DISCARD(char *,spnego->negTokenInit.mechTypes[i]));
326                         }
327                         talloc_free(spnego->negTokenInit.mechTypes);
328                 }
329                 data_blob_free(&spnego->negTokenInit.mechToken);
330                 data_blob_free(&spnego->negTokenInit.mechListMIC);
331                 break;
332         case SPNEGO_NEG_TOKEN_TARG:
333                 if (spnego->negTokenTarg.supportedMech) {
334                         talloc_free(spnego->negTokenTarg.supportedMech);
335                 }
336                 data_blob_free(&spnego->negTokenTarg.responseToken);
337                 data_blob_free(&spnego->negTokenTarg.mechListMIC);
338                 break;
339         default:
340                 ret = False;
341                 break;
342         }
343         ZERO_STRUCTP(spnego);
344 out:
345         return ret;
346 }