lib: Remove tdb_errorstr_compat
[Samba.git] / libcli / auth / spnego_parse.c
blobd4c5bdcfa5c2f266c828d9341df5d0f88db156a5
1 /*
2 Unix SMB/CIFS implementation.
4 RFC2478 Compliant SPNEGO implementation
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
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.
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.
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/>.
23 #include "includes.h"
24 #include "../libcli/auth/spnego.h"
25 #include "../lib/util/asn1.h"
27 static bool read_negTokenInit(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
28 struct spnego_negTokenInit *token)
30 ZERO_STRUCTP(token);
32 if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
33 if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
35 while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
36 int i;
37 uint8_t context;
39 if (!asn1_peek_uint8(asn1, &context)) {
40 asn1->has_error = true;
41 break;
44 switch (context) {
45 /* Read mechTypes */
46 case ASN1_CONTEXT(0): {
47 const char **mechTypes;
49 if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
50 if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
52 mechTypes = talloc(mem_ctx, const char *);
53 if (mechTypes == NULL) {
54 asn1->has_error = true;
55 return false;
57 for (i = 0; !asn1->has_error &&
58 0 < asn1_tag_remaining(asn1); i++) {
59 char *oid;
60 const char **p;
61 p = talloc_realloc(mem_ctx,
62 mechTypes,
63 const char *, i+2);
64 if (p == NULL) {
65 talloc_free(mechTypes);
66 asn1->has_error = true;
67 return false;
69 mechTypes = p;
71 if (!asn1_read_OID(asn1, mechTypes, &oid)) return false;
72 mechTypes[i] = oid;
74 mechTypes[i] = NULL;
75 token->mechTypes = mechTypes;
77 asn1_end_tag(asn1);
78 asn1_end_tag(asn1);
79 break;
81 /* Read reqFlags */
82 case ASN1_CONTEXT(1):
83 if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
84 if (!asn1_read_BitString(asn1, mem_ctx, &token->reqFlags,
85 &token->reqFlagsPadding)) return false;
86 if (!asn1_end_tag(asn1)) return false;
87 break;
88 /* Read mechToken */
89 case ASN1_CONTEXT(2):
90 if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false;
91 if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechToken)) return false;
92 if (!asn1_end_tag(asn1)) return false;
93 break;
94 /* Read mecListMIC */
95 case ASN1_CONTEXT(3):
97 uint8_t type_peek;
98 if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false;
99 if (!asn1_peek_uint8(asn1, &type_peek)) {
100 asn1->has_error = true;
101 break;
103 if (type_peek == ASN1_OCTET_STRING) {
104 if (!asn1_read_OctetString(asn1, mem_ctx,
105 &token->mechListMIC)) return false;
106 } else {
107 /* RFC 2478 says we have an Octet String here,
108 but W2k sends something different... */
109 char *mechListMIC;
110 if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
111 if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
112 if (!asn1_read_GeneralString(asn1, mem_ctx, &mechListMIC)) return false;
113 if (!asn1_end_tag(asn1)) return false;
114 if (!asn1_end_tag(asn1)) return false;
116 token->targetPrincipal = mechListMIC;
118 if (!asn1_end_tag(asn1)) return false;
119 break;
121 default:
122 asn1->has_error = true;
123 break;
127 if (!asn1_end_tag(asn1)) return false;
128 if (!asn1_end_tag(asn1)) return false;
130 return !asn1->has_error;
133 static bool write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
135 if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
136 if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
138 /* Write mechTypes */
139 if (token->mechTypes && *token->mechTypes) {
140 int i;
142 if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
143 if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
144 for (i = 0; token->mechTypes[i]; i++) {
145 if (!asn1_write_OID(asn1, token->mechTypes[i])) return false;
147 if (!asn1_pop_tag(asn1)) return false;
148 if (!asn1_pop_tag(asn1)) return false;
151 /* write reqFlags */
152 if (token->reqFlags.length > 0) {
153 if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
154 if (!asn1_write_BitString(asn1, token->reqFlags.data,
155 token->reqFlags.length,
156 token->reqFlagsPadding)) return false;
157 if (!asn1_pop_tag(asn1)) return false;
160 /* write mechToken */
161 if (token->mechToken.data) {
162 if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false;
163 if (!asn1_write_OctetString(asn1, token->mechToken.data,
164 token->mechToken.length)) return false;
165 if (!asn1_pop_tag(asn1)) return false;
168 /* write mechListMIC */
169 if (token->mechListMIC.data) {
170 if (!asn1_push_tag(asn1, ASN1_CONTEXT(3))) return false;
171 #if 0
172 /* This is what RFC 2478 says ... */
173 asn1_write_OctetString(asn1, token->mechListMIC.data,
174 token->mechListMIC.length);
175 #else
176 /* ... but unfortunately this is what Windows
177 sends/expects */
178 if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
179 if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
180 if (!asn1_push_tag(asn1, ASN1_GENERAL_STRING)) return false;
181 if (!asn1_write(asn1, token->mechListMIC.data,
182 token->mechListMIC.length)) return false;
183 if (!asn1_pop_tag(asn1)) return false;
184 if (!asn1_pop_tag(asn1)) return false;
185 if (!asn1_pop_tag(asn1)) return false;
186 #endif
187 if (!asn1_pop_tag(asn1)) return false;
190 if (!asn1_pop_tag(asn1)) return false;
191 if (!asn1_pop_tag(asn1)) return false;
193 return !asn1->has_error;
196 static bool read_negTokenTarg(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
197 struct spnego_negTokenTarg *token)
199 ZERO_STRUCTP(token);
201 if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
202 if (!asn1_start_tag(asn1, ASN1_SEQUENCE(0))) return false;
204 while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
205 uint8_t context;
206 char *oid;
207 if (!asn1_peek_uint8(asn1, &context)) {
208 asn1->has_error = true;
209 break;
212 switch (context) {
213 case ASN1_CONTEXT(0):
214 if (!asn1_start_tag(asn1, ASN1_CONTEXT(0))) return false;
215 if (!asn1_start_tag(asn1, ASN1_ENUMERATED)) return false;
216 if (!asn1_read_uint8(asn1, &token->negResult)) return false;
217 if (!asn1_end_tag(asn1)) return false;
218 if (!asn1_end_tag(asn1)) return false;
219 break;
220 case ASN1_CONTEXT(1):
221 if (!asn1_start_tag(asn1, ASN1_CONTEXT(1))) return false;
222 if (!asn1_read_OID(asn1, mem_ctx, &oid)) return false;
223 token->supportedMech = oid;
224 if (!asn1_end_tag(asn1)) return false;
225 break;
226 case ASN1_CONTEXT(2):
227 if (!asn1_start_tag(asn1, ASN1_CONTEXT(2))) return false;
228 if (!asn1_read_OctetString(asn1, mem_ctx, &token->responseToken)) return false;
229 if (!asn1_end_tag(asn1)) return false;
230 break;
231 case ASN1_CONTEXT(3):
232 if (!asn1_start_tag(asn1, ASN1_CONTEXT(3))) return false;
233 if (!asn1_read_OctetString(asn1, mem_ctx, &token->mechListMIC)) return false;
234 if (!asn1_end_tag(asn1)) return false;
235 break;
236 default:
237 asn1->has_error = true;
238 break;
242 if (!asn1_end_tag(asn1)) return false;
243 if (!asn1_end_tag(asn1)) return false;
245 return !asn1->has_error;
248 static bool write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
250 if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
251 if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) return false;
253 if (token->negResult != SPNEGO_NONE_RESULT) {
254 if (!asn1_push_tag(asn1, ASN1_CONTEXT(0))) return false;
255 if (!asn1_write_enumerated(asn1, token->negResult)) return false;
256 if (!asn1_pop_tag(asn1)) return false;
259 if (token->supportedMech) {
260 if (!asn1_push_tag(asn1, ASN1_CONTEXT(1))) return false;
261 if (!asn1_write_OID(asn1, token->supportedMech)) return false;
262 if (!asn1_pop_tag(asn1)) return false;
265 if (token->responseToken.data) {
266 if (!asn1_push_tag(asn1, ASN1_CONTEXT(2))) return false;
267 if (!asn1_write_OctetString(asn1, token->responseToken.data,
268 token->responseToken.length)) return false;
269 if (!asn1_pop_tag(asn1)) return false;
272 if (token->mechListMIC.data) {
273 if (!asn1_push_tag(asn1, ASN1_CONTEXT(3))) return false;
274 if (!asn1_write_OctetString(asn1, token->mechListMIC.data,
275 token->mechListMIC.length)) return false;
276 if (!asn1_pop_tag(asn1)) return false;
279 if (!asn1_pop_tag(asn1)) return false;
280 if (!asn1_pop_tag(asn1)) return false;
282 return !asn1->has_error;
285 ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data *token)
287 struct asn1_data *asn1;
288 ssize_t ret = -1;
289 uint8_t context;
291 ZERO_STRUCTP(token);
293 if (data.length == 0) {
294 return ret;
297 asn1 = asn1_init(mem_ctx);
298 if (asn1 == NULL) {
299 return -1;
302 if (!asn1_load(asn1, data)) goto err;
304 if (!asn1_peek_uint8(asn1, &context)) {
305 asn1->has_error = true;
306 } else {
307 switch (context) {
308 case ASN1_APPLICATION(0):
309 if (!asn1_start_tag(asn1, ASN1_APPLICATION(0))) goto err;
310 if (!asn1_check_OID(asn1, OID_SPNEGO)) goto err;
311 if (read_negTokenInit(asn1, mem_ctx, &token->negTokenInit)) {
312 token->type = SPNEGO_NEG_TOKEN_INIT;
314 if (!asn1_end_tag(asn1)) goto err;
315 break;
316 case ASN1_CONTEXT(1):
317 if (read_negTokenTarg(asn1, mem_ctx, &token->negTokenTarg)) {
318 token->type = SPNEGO_NEG_TOKEN_TARG;
320 break;
321 default:
322 asn1->has_error = true;
323 break;
327 if (!asn1->has_error) ret = asn1->ofs;
329 err:
331 asn1_free(asn1);
333 return ret;
336 ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
338 struct asn1_data *asn1 = asn1_init(mem_ctx);
339 ssize_t ret = -1;
341 if (asn1 == NULL) {
342 return -1;
345 switch (spnego->type) {
346 case SPNEGO_NEG_TOKEN_INIT:
347 if (!asn1_push_tag(asn1, ASN1_APPLICATION(0))) goto err;
348 if (!asn1_write_OID(asn1, OID_SPNEGO)) goto err;
349 if (!write_negTokenInit(asn1, &spnego->negTokenInit)) goto err;
350 if (!asn1_pop_tag(asn1)) goto err;
351 break;
352 case SPNEGO_NEG_TOKEN_TARG:
353 write_negTokenTarg(asn1, &spnego->negTokenTarg);
354 break;
355 default:
356 asn1->has_error = true;
357 break;
360 if (!asn1->has_error) {
361 *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
362 ret = asn1->ofs;
365 err:
367 asn1_free(asn1);
369 return ret;
372 bool spnego_free_data(struct spnego_data *spnego)
374 bool ret = true;
376 if (!spnego) goto out;
378 switch(spnego->type) {
379 case SPNEGO_NEG_TOKEN_INIT:
380 if (spnego->negTokenInit.mechTypes) {
381 talloc_free(discard_const(spnego->negTokenInit.mechTypes));
383 data_blob_free(&spnego->negTokenInit.reqFlags);
384 data_blob_free(&spnego->negTokenInit.mechToken);
385 data_blob_free(&spnego->negTokenInit.mechListMIC);
386 talloc_free(spnego->negTokenInit.targetPrincipal);
387 break;
388 case SPNEGO_NEG_TOKEN_TARG:
389 if (spnego->negTokenTarg.supportedMech) {
390 talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
392 data_blob_free(&spnego->negTokenTarg.responseToken);
393 data_blob_free(&spnego->negTokenTarg.mechListMIC);
394 break;
395 default:
396 ret = false;
397 break;
399 ZERO_STRUCTP(spnego);
400 out:
401 return ret;
404 bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
405 const char * const *mech_types,
406 DATA_BLOB *blob)
408 bool ret = false;
409 struct asn1_data *asn1 = asn1_init(mem_ctx);
411 if (asn1 == NULL) {
412 return false;
415 /* Write mechTypes */
416 if (mech_types && *mech_types) {
417 int i;
419 if (!asn1_push_tag(asn1, ASN1_SEQUENCE(0))) goto err;
420 for (i = 0; mech_types[i]; i++) {
421 if (!asn1_write_OID(asn1, mech_types[i])) goto err;
423 if (!asn1_pop_tag(asn1)) goto err;
426 if (asn1->has_error) {
427 goto err;
430 *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
431 if (blob->length != asn1->length) {
432 goto err;
435 ret = true;
437 err:
439 asn1_free(asn1);
441 return ret;