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