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 2 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, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "auth/auth.h"
28 static BOOL read_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *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)) {
38 if (!asn1_peek_uint8(asn1, &context)) {
39 asn1->has_error = True;
46 asn1_start_tag(asn1, ASN1_CONTEXT(0));
47 asn1_start_tag(asn1, ASN1_SEQUENCE(0));
49 token->mechTypes = talloc(NULL, const char *);
50 for (i = 0; !asn1->has_error &&
51 0 < asn1_tag_remaining(asn1); i++) {
52 token->mechTypes = talloc_realloc(NULL,
55 asn1_read_OID(asn1, token->mechTypes + i);
56 if (token->mechTypes[i]) {
57 talloc_steal(token->mechTypes,
61 token->mechTypes[i] = NULL;
68 asn1_start_tag(asn1, ASN1_CONTEXT(1));
69 asn1_read_Integer(asn1, &token->reqFlags);
70 token->reqFlags |= SPNEGO_REQ_FLAG;
75 asn1_start_tag(asn1, ASN1_CONTEXT(2));
76 asn1_read_OctetString(asn1, &token->mechToken);
83 asn1_start_tag(asn1, ASN1_CONTEXT(3));
84 if (!asn1_peek_uint8(asn1, &type_peek)) {
85 asn1->has_error = True;
88 if (type_peek == ASN1_OCTET_STRING) {
89 asn1_read_OctetString(asn1,
92 /* RFC 2478 says we have an Octet String here,
93 but W2k sends something different... */
95 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
96 asn1_push_tag(asn1, ASN1_CONTEXT(0));
97 asn1_read_GeneralString(asn1, &mechListMIC);
101 token->targetPrincipal = mechListMIC;
107 asn1->has_error = True;
115 return !asn1->has_error;
118 static BOOL write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
120 asn1_push_tag(asn1, ASN1_CONTEXT(0));
121 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
123 /* Write mechTypes */
124 if (token->mechTypes && *token->mechTypes) {
127 asn1_push_tag(asn1, ASN1_CONTEXT(0));
128 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
129 for (i = 0; token->mechTypes[i]; i++) {
130 asn1_write_OID(asn1, token->mechTypes[i]);
137 if (token->reqFlags & SPNEGO_REQ_FLAG) {
138 int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
140 asn1_push_tag(asn1, ASN1_CONTEXT(1));
141 asn1_write_Integer(asn1, flags);
145 /* write mechToken */
146 if (token->mechToken.data) {
147 asn1_push_tag(asn1, ASN1_CONTEXT(2));
148 asn1_write_OctetString(asn1, token->mechToken.data,
149 token->mechToken.length);
153 /* write mechListMIC */
154 if (token->mechListMIC.data) {
155 asn1_push_tag(asn1, ASN1_CONTEXT(3));
157 /* This is what RFC 2478 says ... */
158 asn1_write_OctetString(asn1, token->mechListMIC.data,
159 token->mechListMIC.length);
161 /* ... but unfortunately this is what Windows
163 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
164 asn1_push_tag(asn1, ASN1_CONTEXT(0));
165 asn1_push_tag(asn1, ASN1_GENERAL_STRING);
166 asn1_write(asn1, token->mechListMIC.data,
167 token->mechListMIC.length);
178 return !asn1->has_error;
181 static BOOL read_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
185 asn1_start_tag(asn1, ASN1_CONTEXT(1));
186 asn1_start_tag(asn1, ASN1_SEQUENCE(0));
188 while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
190 if (!asn1_peek_uint8(asn1, &context)) {
191 asn1->has_error = True;
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);
203 case ASN1_CONTEXT(1):
204 asn1_start_tag(asn1, ASN1_CONTEXT(1));
205 asn1_read_OID(asn1, &token->supportedMech);
208 case ASN1_CONTEXT(2):
209 asn1_start_tag(asn1, ASN1_CONTEXT(2));
210 asn1_read_OctetString(asn1, &token->responseToken);
213 case ASN1_CONTEXT(3):
214 asn1_start_tag(asn1, ASN1_CONTEXT(3));
215 asn1_read_OctetString(asn1, &token->mechListMIC);
219 asn1->has_error = True;
227 return !asn1->has_error;
230 static BOOL write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
232 asn1_push_tag(asn1, ASN1_CONTEXT(1));
233 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
235 if (token->negResult != SPNEGO_NONE_RESULT) {
236 asn1_push_tag(asn1, ASN1_CONTEXT(0));
237 asn1_write_enumerated(asn1, token->negResult);
241 if (token->supportedMech) {
242 asn1_push_tag(asn1, ASN1_CONTEXT(1));
243 asn1_write_OID(asn1, token->supportedMech);
247 if (token->responseToken.data) {
248 asn1_push_tag(asn1, ASN1_CONTEXT(2));
249 asn1_write_OctetString(asn1, token->responseToken.data,
250 token->responseToken.length);
254 if (token->mechListMIC.data) {
255 asn1_push_tag(asn1, ASN1_CONTEXT(3));
256 asn1_write_OctetString(asn1, token->mechListMIC.data,
257 token->mechListMIC.length);
264 return !asn1->has_error;
267 ssize_t spnego_read_data(DATA_BLOB data, struct spnego_data *token)
269 struct asn1_data asn1;
276 if (data.length == 0) {
280 asn1_load(&asn1, data);
282 if (!asn1_peek_uint8(&asn1, &context)) {
283 asn1.has_error = True;
286 case ASN1_APPLICATION(0):
287 asn1_start_tag(&asn1, ASN1_APPLICATION(0));
288 asn1_check_OID(&asn1, GENSEC_OID_SPNEGO);
289 if (read_negTokenInit(&asn1, &token->negTokenInit)) {
290 token->type = SPNEGO_NEG_TOKEN_INIT;
294 case ASN1_CONTEXT(1):
295 if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
296 token->type = SPNEGO_NEG_TOKEN_TARG;
300 asn1.has_error = True;
305 if (!asn1.has_error) ret = asn1.ofs;
311 ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
313 struct asn1_data asn1;
318 switch (spnego->type) {
319 case SPNEGO_NEG_TOKEN_INIT:
320 asn1_push_tag(&asn1, ASN1_APPLICATION(0));
321 asn1_write_OID(&asn1, GENSEC_OID_SPNEGO);
322 write_negTokenInit(&asn1, &spnego->negTokenInit);
325 case SPNEGO_NEG_TOKEN_TARG:
326 write_negTokenTarg(&asn1, &spnego->negTokenTarg);
329 asn1.has_error = True;
333 if (!asn1.has_error) {
334 *blob = data_blob_talloc(mem_ctx, asn1.data, asn1.length);
342 BOOL spnego_free_data(struct spnego_data *spnego)
346 if (!spnego) goto out;
348 switch(spnego->type) {
349 case SPNEGO_NEG_TOKEN_INIT:
350 if (spnego->negTokenInit.mechTypes) {
351 talloc_free(spnego->negTokenInit.mechTypes);
353 data_blob_free(&spnego->negTokenInit.mechToken);
354 data_blob_free(&spnego->negTokenInit.mechListMIC);
355 talloc_free(spnego->negTokenInit.targetPrincipal);
357 case SPNEGO_NEG_TOKEN_TARG:
358 if (spnego->negTokenTarg.supportedMech) {
359 talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
361 data_blob_free(&spnego->negTokenTarg.responseToken);
362 data_blob_free(&spnego->negTokenTarg.mechListMIC);
368 ZERO_STRUCTP(spnego);