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.
27 #define DBGC_CLASS DBGC_AUTH
29 static BOOL read_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token)
33 asn1_start_tag(asn1, ASN1_CONTEXT(0));
34 asn1_start_tag(asn1, ASN1_SEQUENCE(0));
36 while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
39 switch (asn1->data[asn1->ofs]) {
42 asn1_start_tag(asn1, ASN1_CONTEXT(0));
43 asn1_start_tag(asn1, ASN1_SEQUENCE(0));
45 token->mechTypes = SMB_MALLOC_P(const char *);
46 for (i = 0; !asn1->has_error &&
47 0 < asn1_tag_remaining(asn1); i++) {
49 SMB_REALLOC_ARRAY(token->mechTypes, const char *, i + 2);
51 CONST_DISCARD(char **,
52 (token->mechTypes + i)));
54 token->mechTypes[i] = NULL;
61 asn1_start_tag(asn1, ASN1_CONTEXT(1));
62 asn1_read_Integer(asn1, &token->reqFlags);
63 token->reqFlags |= SPNEGO_REQ_FLAG;
68 asn1_start_tag(asn1, ASN1_CONTEXT(2));
69 asn1_read_OctetString(asn1, &token->mechToken);
74 asn1_start_tag(asn1, ASN1_CONTEXT(3));
75 if (asn1->data[asn1->ofs] == ASN1_OCTET_STRING) {
76 asn1_read_OctetString(asn1,
79 /* RFC 2478 says we have an Octet String here,
80 but W2k sends something different... */
82 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
83 asn1_push_tag(asn1, ASN1_CONTEXT(0));
84 asn1_read_GeneralString(asn1, &mechListMIC);
89 data_blob(mechListMIC, strlen(mechListMIC));
90 SAFE_FREE(mechListMIC);
95 asn1->has_error = True;
103 return !asn1->has_error;
106 static BOOL write_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token)
108 asn1_push_tag(asn1, ASN1_CONTEXT(0));
109 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
111 /* Write mechTypes */
112 if (token->mechTypes && *token->mechTypes) {
115 asn1_push_tag(asn1, ASN1_CONTEXT(0));
116 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
117 for (i = 0; token->mechTypes[i]; i++) {
118 asn1_write_OID(asn1, token->mechTypes[i]);
125 if (token->reqFlags & SPNEGO_REQ_FLAG) {
126 int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
128 asn1_push_tag(asn1, ASN1_CONTEXT(1));
129 asn1_write_Integer(asn1, flags);
133 /* write mechToken */
134 if (token->mechToken.data) {
135 asn1_push_tag(asn1, ASN1_CONTEXT(2));
136 asn1_write_OctetString(asn1, token->mechToken.data,
137 token->mechToken.length);
141 /* write mechListMIC */
142 if (token->mechListMIC.data) {
143 asn1_push_tag(asn1, ASN1_CONTEXT(3));
145 /* This is what RFC 2478 says ... */
146 asn1_write_OctetString(asn1, token->mechListMIC.data,
147 token->mechListMIC.length);
149 /* ... but unfortunately this is what Windows
151 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
152 asn1_push_tag(asn1, ASN1_CONTEXT(0));
153 asn1_push_tag(asn1, ASN1_GENERAL_STRING);
154 asn1_write(asn1, token->mechListMIC.data,
155 token->mechListMIC.length);
166 return !asn1->has_error;
169 static BOOL read_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
173 asn1_start_tag(asn1, ASN1_CONTEXT(1));
174 asn1_start_tag(asn1, ASN1_SEQUENCE(0));
176 while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
177 switch (asn1->data[asn1->ofs]) {
178 case ASN1_CONTEXT(0):
179 asn1_start_tag(asn1, ASN1_CONTEXT(0));
180 asn1_start_tag(asn1, ASN1_ENUMERATED);
181 asn1_read_uint8(asn1, &token->negResult);
185 case ASN1_CONTEXT(1):
186 asn1_start_tag(asn1, ASN1_CONTEXT(1));
187 asn1_read_OID(asn1, CONST_DISCARD(char **, &token->supportedMech));
190 case ASN1_CONTEXT(2):
191 asn1_start_tag(asn1, ASN1_CONTEXT(2));
192 asn1_read_OctetString(asn1, &token->responseToken);
195 case ASN1_CONTEXT(3):
196 asn1_start_tag(asn1, ASN1_CONTEXT(3));
197 asn1_read_OctetString(asn1, &token->mechListMIC);
201 asn1->has_error = True;
209 return !asn1->has_error;
212 static BOOL write_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
214 asn1_push_tag(asn1, ASN1_CONTEXT(1));
215 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
217 asn1_push_tag(asn1, ASN1_CONTEXT(0));
218 asn1_write_enumerated(asn1, token->negResult);
221 if (token->supportedMech) {
222 asn1_push_tag(asn1, ASN1_CONTEXT(1));
223 asn1_write_OID(asn1, token->supportedMech);
227 if (token->responseToken.data) {
228 asn1_push_tag(asn1, ASN1_CONTEXT(2));
229 asn1_write_OctetString(asn1, token->responseToken.data,
230 token->responseToken.length);
234 if (token->mechListMIC.data) {
235 asn1_push_tag(asn1, ASN1_CONTEXT(3));
236 asn1_write_OctetString(asn1, token->mechListMIC.data,
237 token->mechListMIC.length);
244 return !asn1->has_error;
247 ssize_t read_spnego_data(DATA_BLOB data, SPNEGO_DATA *token)
254 asn1_load(&asn1, data);
256 switch (asn1.data[asn1.ofs]) {
257 case ASN1_APPLICATION(0):
258 asn1_start_tag(&asn1, ASN1_APPLICATION(0));
259 asn1_check_OID(&asn1, OID_SPNEGO);
260 if (read_negTokenInit(&asn1, &token->negTokenInit)) {
261 token->type = SPNEGO_NEG_TOKEN_INIT;
265 case ASN1_CONTEXT(1):
266 if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
267 token->type = SPNEGO_NEG_TOKEN_TARG;
274 if (!asn1.has_error) ret = asn1.ofs;
280 ssize_t write_spnego_data(DATA_BLOB *blob, SPNEGO_DATA *spnego)
287 switch (spnego->type) {
288 case SPNEGO_NEG_TOKEN_INIT:
289 asn1_push_tag(&asn1, ASN1_APPLICATION(0));
290 asn1_write_OID(&asn1, OID_SPNEGO);
291 write_negTokenInit(&asn1, &spnego->negTokenInit);
294 case SPNEGO_NEG_TOKEN_TARG:
295 write_negTokenTarg(&asn1, &spnego->negTokenTarg);
298 asn1.has_error = True;
302 if (!asn1.has_error) {
303 *blob = data_blob(asn1.data, asn1.length);
311 BOOL free_spnego_data(SPNEGO_DATA *spnego)
315 if (!spnego) goto out;
317 switch(spnego->type) {
318 case SPNEGO_NEG_TOKEN_INIT:
319 if (spnego->negTokenInit.mechTypes) {
321 for (i = 0; spnego->negTokenInit.mechTypes[i]; i++) {
322 free(CONST_DISCARD(void *,
323 spnego->negTokenInit.mechTypes[i]));
325 free(spnego->negTokenInit.mechTypes);
327 data_blob_free(&spnego->negTokenInit.mechToken);
328 data_blob_free(&spnego->negTokenInit.mechListMIC);
330 case SPNEGO_NEG_TOKEN_TARG:
331 if (spnego->negTokenTarg.supportedMech) {
332 free(CONST_DISCARD(void *, spnego->negTokenTarg.supportedMech));
334 data_blob_free(&spnego->negTokenTarg.responseToken);
335 data_blob_free(&spnego->negTokenTarg.mechListMIC);
341 ZERO_STRUCTP(spnego);