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