docs: mention --update and --encrypt in smbget manpage.
[Samba.git] / libcli / auth / spnego_parse.c
blobb1ca07d6376169dfb79a843888237efe6e5fa50d
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 asn1_start_tag(asn1, ASN1_CONTEXT(0));
33 asn1_start_tag(asn1, ASN1_SEQUENCE(0));
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;
43 switch (context) {
44 /* Read mechTypes */
45 case ASN1_CONTEXT(0): {
46 const char **mechTypes;
48 asn1_start_tag(asn1, ASN1_CONTEXT(0));
49 asn1_start_tag(asn1, ASN1_SEQUENCE(0));
51 mechTypes = talloc(mem_ctx, const char *);
52 if (mechTypes == NULL) {
53 asn1->has_error = true;
54 return false;
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;
68 mechTypes = p;
70 asn1_read_OID(asn1, mechTypes, &oid);
71 mechTypes[i] = oid;
73 mechTypes[i] = NULL;
74 token->mechTypes = mechTypes;
76 asn1_end_tag(asn1);
77 asn1_end_tag(asn1);
78 break;
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):
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;
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);
115 token->targetPrincipal = mechListMIC;
117 asn1_end_tag(asn1);
118 break;
120 default:
121 asn1->has_error = true;
122 break;
126 asn1_end_tag(asn1);
127 asn1_end_tag(asn1);
129 return !asn1->has_error;
132 static bool write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
134 asn1_push_tag(asn1, ASN1_CONTEXT(0));
135 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
137 /* Write mechTypes */
138 if (token->mechTypes && *token->mechTypes) {
139 int i;
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]);
146 asn1_pop_tag(asn1);
147 asn1_pop_tag(asn1);
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);
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);
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);
189 asn1_pop_tag(asn1);
190 asn1_pop_tag(asn1);
192 return !asn1->has_error;
195 static bool read_negTokenTarg(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
196 struct spnego_negTokenTarg *token)
198 ZERO_STRUCTP(token);
200 asn1_start_tag(asn1, ASN1_CONTEXT(1));
201 asn1_start_tag(asn1, ASN1_SEQUENCE(0));
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;
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;
241 asn1_end_tag(asn1);
242 asn1_end_tag(asn1);
244 return !asn1->has_error;
247 static bool write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
249 asn1_push_tag(asn1, ASN1_CONTEXT(1));
250 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
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);
258 if (token->supportedMech) {
259 asn1_push_tag(asn1, ASN1_CONTEXT(1));
260 asn1_write_OID(asn1, token->supportedMech);
261 asn1_pop_tag(asn1);
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);
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);
278 asn1_pop_tag(asn1);
279 asn1_pop_tag(asn1);
281 return !asn1->has_error;
284 ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data *token)
286 struct asn1_data *asn1;
287 ssize_t ret = -1;
288 uint8_t context;
290 ZERO_STRUCTP(token);
292 if (data.length == 0) {
293 return ret;
296 asn1 = asn1_init(mem_ctx);
297 if (asn1 == NULL) {
298 return -1;
301 asn1_load(asn1, data);
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;
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;
319 break;
320 default:
321 asn1->has_error = true;
322 break;
326 if (!asn1->has_error) ret = asn1->ofs;
327 asn1_free(asn1);
329 return ret;
332 ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
334 struct asn1_data *asn1 = asn1_init(mem_ctx);
335 ssize_t ret = -1;
337 if (asn1 == NULL) {
338 return -1;
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;
356 if (!asn1->has_error) {
357 *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
358 ret = asn1->ofs;
360 asn1_free(asn1);
362 return ret;
365 bool spnego_free_data(struct spnego_data *spnego)
367 bool ret = true;
369 if (!spnego) goto out;
371 switch(spnego->type) {
372 case SPNEGO_NEG_TOKEN_INIT:
373 if (spnego->negTokenInit.mechTypes) {
374 talloc_free(discard_const(spnego->negTokenInit.mechTypes));
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));
385 data_blob_free(&spnego->negTokenTarg.responseToken);
386 data_blob_free(&spnego->negTokenTarg.mechListMIC);
387 break;
388 default:
389 ret = false;
390 break;
392 ZERO_STRUCTP(spnego);
393 out:
394 return ret;
397 bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
398 const char * const *mech_types,
399 DATA_BLOB *blob)
401 struct asn1_data *asn1 = asn1_init(mem_ctx);
403 if (asn1 == NULL) {
404 return false;
407 /* Write mechTypes */
408 if (mech_types && *mech_types) {
409 int i;
411 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
412 for (i = 0; mech_types[i]; i++) {
413 asn1_write_OID(asn1, mech_types[i]);
415 asn1_pop_tag(asn1);
418 if (asn1->has_error) {
419 asn1_free(asn1);
420 return false;
423 *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
424 if (blob->length != asn1->length) {
425 asn1_free(asn1);
426 return false;
429 asn1_free(asn1);
431 return true;