3bf7aeab6277c76576a9a6f75b6b5582f12b3177
[mat/samba.git] / libcli / auth / spnego_parse.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 #include "../libcli/auth/spnego.h"
25 #include "../lib/util/asn1.h"
26
27 static bool read_negTokenInit(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
28                               struct spnego_negTokenInit *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                 uint8_t context;
38                 if (!asn1_peek_uint8(asn1, &context)) {
39                         asn1->has_error = true;
40                         break;
41                 }
42
43                 switch (context) {
44                 /* Read mechTypes */
45                 case ASN1_CONTEXT(0):
46                         asn1_start_tag(asn1, ASN1_CONTEXT(0));
47                         asn1_start_tag(asn1, ASN1_SEQUENCE(0));
48
49                         token->mechTypes = talloc(NULL, const char *);
50                         for (i = 0; !asn1->has_error &&
51                                      0 < asn1_tag_remaining(asn1); i++) {
52                                 char *oid;
53                                 token->mechTypes = talloc_realloc(NULL,
54                                                                   token->mechTypes,
55                                                                   const char *, i+2);
56                                 asn1_read_OID(asn1, token->mechTypes, &oid);
57                                 token->mechTypes[i] = oid;
58                         }
59                         token->mechTypes[i] = NULL;
60
61                         asn1_end_tag(asn1);
62                         asn1_end_tag(asn1);
63                         break;
64                 /* Read reqFlags */
65                 case ASN1_CONTEXT(1):
66                         asn1_start_tag(asn1, ASN1_CONTEXT(1));
67                         asn1_read_BitString(asn1, mem_ctx, &token->reqFlags,
68                                             &token->reqFlagsPadding);
69                         asn1_end_tag(asn1);
70                         break;
71                 /* Read mechToken */
72                 case ASN1_CONTEXT(2):
73                         asn1_start_tag(asn1, ASN1_CONTEXT(2));
74                         asn1_read_OctetString(asn1, mem_ctx, &token->mechToken);
75                         asn1_end_tag(asn1);
76                         break;
77                 /* Read mecListMIC */
78                 case ASN1_CONTEXT(3):
79                 {
80                         uint8_t type_peek;
81                         asn1_start_tag(asn1, ASN1_CONTEXT(3));
82                         if (!asn1_peek_uint8(asn1, &type_peek)) {
83                                 asn1->has_error = true;
84                                 break;
85                         }
86                         if (type_peek == ASN1_OCTET_STRING) {
87                                 asn1_read_OctetString(asn1, mem_ctx,
88                                                       &token->mechListMIC);
89                         } else {
90                                 /* RFC 2478 says we have an Octet String here,
91                                    but W2k sends something different... */
92                                 char *mechListMIC;
93                                 asn1_start_tag(asn1, ASN1_SEQUENCE(0));
94                                 asn1_start_tag(asn1, ASN1_CONTEXT(0));
95                                 asn1_read_GeneralString(asn1, mem_ctx, &mechListMIC);
96                                 asn1_end_tag(asn1);
97                                 asn1_end_tag(asn1);
98
99                                 token->targetPrincipal = mechListMIC;
100                         }
101                         asn1_end_tag(asn1);
102                         break;
103                 }
104                 default:
105                         asn1->has_error = true;
106                         break;
107                 }
108         }
109
110         asn1_end_tag(asn1);
111         asn1_end_tag(asn1);
112
113         return !asn1->has_error;
114 }
115
116 static bool write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
117 {
118         asn1_push_tag(asn1, ASN1_CONTEXT(0));
119         asn1_push_tag(asn1, ASN1_SEQUENCE(0));
120
121         /* Write mechTypes */
122         if (token->mechTypes && *token->mechTypes) {
123                 int i;
124
125                 asn1_push_tag(asn1, ASN1_CONTEXT(0));
126                 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
127                 for (i = 0; token->mechTypes[i]; i++) {
128                         asn1_write_OID(asn1, token->mechTypes[i]);
129                 }
130                 asn1_pop_tag(asn1);
131                 asn1_pop_tag(asn1);
132         }
133
134         /* write reqFlags */
135         if (token->reqFlags.length > 0) {
136                 asn1_push_tag(asn1, ASN1_CONTEXT(1));
137                 asn1_write_BitString(asn1, token->reqFlags.data,
138                                      token->reqFlags.length,
139                                      token->reqFlagsPadding);
140                 asn1_pop_tag(asn1);
141         }
142
143         /* write mechToken */
144         if (token->mechToken.data) {
145                 asn1_push_tag(asn1, ASN1_CONTEXT(2));
146                 asn1_write_OctetString(asn1, token->mechToken.data,
147                                        token->mechToken.length);
148                 asn1_pop_tag(asn1);
149         }
150
151         /* write mechListMIC */
152         if (token->mechListMIC.data) {
153                 asn1_push_tag(asn1, ASN1_CONTEXT(3));
154 #if 0
155                 /* This is what RFC 2478 says ... */
156                 asn1_write_OctetString(asn1, token->mechListMIC.data,
157                                        token->mechListMIC.length);
158 #else
159                 /* ... but unfortunately this is what Windows
160                    sends/expects */
161                 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
162                 asn1_push_tag(asn1, ASN1_CONTEXT(0));
163                 asn1_push_tag(asn1, ASN1_GENERAL_STRING);
164                 asn1_write(asn1, token->mechListMIC.data,
165                            token->mechListMIC.length);
166                 asn1_pop_tag(asn1);
167                 asn1_pop_tag(asn1);
168                 asn1_pop_tag(asn1);
169 #endif
170                 asn1_pop_tag(asn1);
171         }
172
173         asn1_pop_tag(asn1);
174         asn1_pop_tag(asn1);
175
176         return !asn1->has_error;
177 }
178
179 static bool read_negTokenTarg(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
180                               struct spnego_negTokenTarg *token)
181 {
182         ZERO_STRUCTP(token);
183
184         asn1_start_tag(asn1, ASN1_CONTEXT(1));
185         asn1_start_tag(asn1, ASN1_SEQUENCE(0));
186
187         while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
188                 uint8_t context;
189                 char *oid;
190                 if (!asn1_peek_uint8(asn1, &context)) {
191                         asn1->has_error = true;
192                         break;
193                 }
194
195                 switch (context) {
196                 case ASN1_CONTEXT(0):
197                         asn1_start_tag(asn1, ASN1_CONTEXT(0));
198                         asn1_start_tag(asn1, ASN1_ENUMERATED);
199                         asn1_read_uint8(asn1, &token->negResult);
200                         asn1_end_tag(asn1);
201                         asn1_end_tag(asn1);
202                         break;
203                 case ASN1_CONTEXT(1):
204                         asn1_start_tag(asn1, ASN1_CONTEXT(1));
205                         asn1_read_OID(asn1, mem_ctx, &oid);
206                         token->supportedMech = oid;
207                         asn1_end_tag(asn1);
208                         break;
209                 case ASN1_CONTEXT(2):
210                         asn1_start_tag(asn1, ASN1_CONTEXT(2));
211                         asn1_read_OctetString(asn1, mem_ctx, &token->responseToken);
212                         asn1_end_tag(asn1);
213                         break;
214                 case ASN1_CONTEXT(3):
215                         asn1_start_tag(asn1, ASN1_CONTEXT(3));
216                         asn1_read_OctetString(asn1, mem_ctx, &token->mechListMIC);
217                         asn1_end_tag(asn1);
218                         break;
219                 default:
220                         asn1->has_error = true;
221                         break;
222                 }
223         }
224
225         asn1_end_tag(asn1);
226         asn1_end_tag(asn1);
227
228         return !asn1->has_error;
229 }
230
231 static bool write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
232 {
233         asn1_push_tag(asn1, ASN1_CONTEXT(1));
234         asn1_push_tag(asn1, ASN1_SEQUENCE(0));
235
236         if (token->negResult != SPNEGO_NONE_RESULT) {
237                 asn1_push_tag(asn1, ASN1_CONTEXT(0));
238                 asn1_write_enumerated(asn1, token->negResult);
239                 asn1_pop_tag(asn1);
240         }
241
242         if (token->supportedMech) {
243                 asn1_push_tag(asn1, ASN1_CONTEXT(1));
244                 asn1_write_OID(asn1, token->supportedMech);
245                 asn1_pop_tag(asn1);
246         }
247
248         if (token->responseToken.data) {
249                 asn1_push_tag(asn1, ASN1_CONTEXT(2));
250                 asn1_write_OctetString(asn1, token->responseToken.data,
251                                        token->responseToken.length);
252                 asn1_pop_tag(asn1);
253         }
254
255         if (token->mechListMIC.data) {
256                 asn1_push_tag(asn1, ASN1_CONTEXT(3));
257                 asn1_write_OctetString(asn1, token->mechListMIC.data,
258                                       token->mechListMIC.length);
259                 asn1_pop_tag(asn1);
260         }
261
262         asn1_pop_tag(asn1);
263         asn1_pop_tag(asn1);
264
265         return !asn1->has_error;
266 }
267
268 ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data *token)
269 {
270         struct asn1_data *asn1;
271         ssize_t ret = -1;
272         uint8_t context;
273
274         ZERO_STRUCTP(token);
275
276         if (data.length == 0) {
277                 return ret;
278         }
279
280         asn1 = asn1_init(mem_ctx);
281         if (asn1 == NULL) {
282                 return -1;
283         }
284
285         asn1_load(asn1, data);
286
287         if (!asn1_peek_uint8(asn1, &context)) {
288                 asn1->has_error = true;
289         } else {
290                 switch (context) {
291                 case ASN1_APPLICATION(0):
292                         asn1_start_tag(asn1, ASN1_APPLICATION(0));
293                         asn1_check_OID(asn1, OID_SPNEGO);
294                         if (read_negTokenInit(asn1, mem_ctx, &token->negTokenInit)) {
295                                 token->type = SPNEGO_NEG_TOKEN_INIT;
296                         }
297                         asn1_end_tag(asn1);
298                         break;
299                 case ASN1_CONTEXT(1):
300                         if (read_negTokenTarg(asn1, mem_ctx, &token->negTokenTarg)) {
301                                 token->type = SPNEGO_NEG_TOKEN_TARG;
302                         }
303                         break;
304                 default:
305                         asn1->has_error = true;
306                         break;
307                 }
308         }
309
310         if (!asn1->has_error) ret = asn1->ofs;
311         asn1_free(asn1);
312
313         return ret;
314 }
315
316 ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
317 {
318         struct asn1_data *asn1 = asn1_init(mem_ctx);
319         ssize_t ret = -1;
320
321         if (asn1 == NULL) {
322                 return -1;
323         }
324
325         switch (spnego->type) {
326         case SPNEGO_NEG_TOKEN_INIT:
327                 asn1_push_tag(asn1, ASN1_APPLICATION(0));
328                 asn1_write_OID(asn1, OID_SPNEGO);
329                 write_negTokenInit(asn1, &spnego->negTokenInit);
330                 asn1_pop_tag(asn1);
331                 break;
332         case SPNEGO_NEG_TOKEN_TARG:
333                 write_negTokenTarg(asn1, &spnego->negTokenTarg);
334                 break;
335         default:
336                 asn1->has_error = true;
337                 break;
338         }
339
340         if (!asn1->has_error) {
341                 *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
342                 ret = asn1->ofs;
343         }
344         asn1_free(asn1);
345
346         return ret;
347 }
348
349 bool spnego_free_data(struct spnego_data *spnego)
350 {
351         bool ret = true;
352
353         if (!spnego) goto out;
354
355         switch(spnego->type) {
356         case SPNEGO_NEG_TOKEN_INIT:
357                 if (spnego->negTokenInit.mechTypes) {
358                         talloc_free(spnego->negTokenInit.mechTypes);
359                 }
360                 data_blob_free(&spnego->negTokenInit.reqFlags);
361                 data_blob_free(&spnego->negTokenInit.mechToken);
362                 data_blob_free(&spnego->negTokenInit.mechListMIC);
363                 talloc_free(spnego->negTokenInit.targetPrincipal);
364                 break;
365         case SPNEGO_NEG_TOKEN_TARG:
366                 if (spnego->negTokenTarg.supportedMech) {
367                         talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
368                 }
369                 data_blob_free(&spnego->negTokenTarg.responseToken);
370                 data_blob_free(&spnego->negTokenTarg.mechListMIC);
371                 break;
372         default:
373                 ret = false;
374                 break;
375         }
376         ZERO_STRUCTP(spnego);
377 out:
378         return ret;
379 }
380
381 bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
382                              const char **mech_types,
383                              DATA_BLOB *blob)
384 {
385         struct asn1_data *asn1 = asn1_init(mem_ctx);
386
387         if (asn1 == NULL) {
388                 return false;
389         }
390
391         /* Write mechTypes */
392         if (mech_types && *mech_types) {
393                 int i;
394
395                 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
396                 for (i = 0; mech_types[i]; i++) {
397                         asn1_write_OID(asn1, mech_types[i]);
398                 }
399                 asn1_pop_tag(asn1);
400         }
401
402         if (asn1->has_error) {
403                 asn1_free(asn1);
404                 return false;
405         }
406
407         *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
408         if (blob->length != asn1->length) {
409                 asn1_free(asn1);
410                 return false;
411         }
412
413         asn1_free(asn1);
414
415         return true;
416 }