r6303: Setting up for 3.0.15pre1
[Samba.git] / source / libsmb / spnego.c
blob0387e8f67d84c35ad9c8c40fb949c32b69ea5d6e
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 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.
24 #include "includes.h"
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_AUTH
29 static BOOL read_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token)
31 ZERO_STRUCTP(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)) {
37 int i;
39 switch (asn1->data[asn1->ofs]) {
40 /* Read mechTypes */
41 case ASN1_CONTEXT(0):
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++) {
48 token->mechTypes =
49 SMB_REALLOC_ARRAY(token->mechTypes, const char *, i + 2);
50 asn1_read_OID(asn1,
51 CONST_DISCARD(char **,
52 (token->mechTypes + i)));
54 token->mechTypes[i] = NULL;
56 asn1_end_tag(asn1);
57 asn1_end_tag(asn1);
58 break;
59 /* Read reqFlags */
60 case ASN1_CONTEXT(1):
61 asn1_start_tag(asn1, ASN1_CONTEXT(1));
62 asn1_read_Integer(asn1, &token->reqFlags);
63 token->reqFlags |= SPNEGO_REQ_FLAG;
64 asn1_end_tag(asn1);
65 break;
66 /* Read mechToken */
67 case ASN1_CONTEXT(2):
68 asn1_start_tag(asn1, ASN1_CONTEXT(2));
69 asn1_read_OctetString(asn1, &token->mechToken);
70 asn1_end_tag(asn1);
71 break;
72 /* Read mecListMIC */
73 case ASN1_CONTEXT(3):
74 asn1_start_tag(asn1, ASN1_CONTEXT(3));
75 if (asn1->data[asn1->ofs] == ASN1_OCTET_STRING) {
76 asn1_read_OctetString(asn1,
77 &token->mechListMIC);
78 } else {
79 /* RFC 2478 says we have an Octet String here,
80 but W2k sends something different... */
81 char *mechListMIC;
82 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
83 asn1_push_tag(asn1, ASN1_CONTEXT(0));
84 asn1_read_GeneralString(asn1, &mechListMIC);
85 asn1_pop_tag(asn1);
86 asn1_pop_tag(asn1);
88 token->mechListMIC =
89 data_blob(mechListMIC, strlen(mechListMIC));
90 SAFE_FREE(mechListMIC);
92 asn1_end_tag(asn1);
93 break;
94 default:
95 asn1->has_error = True;
96 break;
100 asn1_end_tag(asn1);
101 asn1_end_tag(asn1);
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) {
113 int i;
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]);
120 asn1_pop_tag(asn1);
121 asn1_pop_tag(asn1);
124 /* write reqFlags */
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);
130 asn1_pop_tag(asn1);
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);
138 asn1_pop_tag(asn1);
141 /* write mechListMIC */
142 if (token->mechListMIC.data) {
143 asn1_push_tag(asn1, ASN1_CONTEXT(3));
144 #if 0
145 /* This is what RFC 2478 says ... */
146 asn1_write_OctetString(asn1, token->mechListMIC.data,
147 token->mechListMIC.length);
148 #else
149 /* ... but unfortunately this is what Windows
150 sends/expects */
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);
156 asn1_pop_tag(asn1);
157 asn1_pop_tag(asn1);
158 asn1_pop_tag(asn1);
159 #endif
160 asn1_pop_tag(asn1);
163 asn1_pop_tag(asn1);
164 asn1_pop_tag(asn1);
166 return !asn1->has_error;
169 static BOOL read_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
171 ZERO_STRUCTP(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);
182 asn1_end_tag(asn1);
183 asn1_end_tag(asn1);
184 break;
185 case ASN1_CONTEXT(1):
186 asn1_start_tag(asn1, ASN1_CONTEXT(1));
187 asn1_read_OID(asn1, CONST_DISCARD(char **, &token->supportedMech));
188 asn1_end_tag(asn1);
189 break;
190 case ASN1_CONTEXT(2):
191 asn1_start_tag(asn1, ASN1_CONTEXT(2));
192 asn1_read_OctetString(asn1, &token->responseToken);
193 asn1_end_tag(asn1);
194 break;
195 case ASN1_CONTEXT(3):
196 asn1_start_tag(asn1, ASN1_CONTEXT(3));
197 asn1_read_OctetString(asn1, &token->mechListMIC);
198 asn1_end_tag(asn1);
199 break;
200 default:
201 asn1->has_error = True;
202 break;
206 asn1_end_tag(asn1);
207 asn1_end_tag(asn1);
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);
219 asn1_pop_tag(asn1);
221 if (token->supportedMech) {
222 asn1_push_tag(asn1, ASN1_CONTEXT(1));
223 asn1_write_OID(asn1, token->supportedMech);
224 asn1_pop_tag(asn1);
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);
231 asn1_pop_tag(asn1);
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);
238 asn1_pop_tag(asn1);
241 asn1_pop_tag(asn1);
242 asn1_pop_tag(asn1);
244 return !asn1->has_error;
247 ssize_t read_spnego_data(DATA_BLOB data, SPNEGO_DATA *token)
249 ASN1_DATA asn1;
250 ssize_t ret = -1;
252 ZERO_STRUCTP(token);
253 ZERO_STRUCT(asn1);
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;
263 asn1_end_tag(&asn1);
264 break;
265 case ASN1_CONTEXT(1):
266 if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
267 token->type = SPNEGO_NEG_TOKEN_TARG;
269 break;
270 default:
271 break;
274 if (!asn1.has_error) ret = asn1.ofs;
275 asn1_free(&asn1);
277 return ret;
280 ssize_t write_spnego_data(DATA_BLOB *blob, SPNEGO_DATA *spnego)
282 ASN1_DATA asn1;
283 ssize_t ret = -1;
285 ZERO_STRUCT(asn1);
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);
292 asn1_pop_tag(&asn1);
293 break;
294 case SPNEGO_NEG_TOKEN_TARG:
295 write_negTokenTarg(&asn1, &spnego->negTokenTarg);
296 break;
297 default:
298 asn1.has_error = True;
299 break;
302 if (!asn1.has_error) {
303 *blob = data_blob(asn1.data, asn1.length);
304 ret = asn1.ofs;
306 asn1_free(&asn1);
308 return ret;
311 BOOL free_spnego_data(SPNEGO_DATA *spnego)
313 BOOL ret = True;
315 if (!spnego) goto out;
317 switch(spnego->type) {
318 case SPNEGO_NEG_TOKEN_INIT:
319 if (spnego->negTokenInit.mechTypes) {
320 int i;
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);
329 break;
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);
336 break;
337 default:
338 ret = False;
339 break;
341 ZERO_STRUCTP(spnego);
342 out:
343 return ret;