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