1/*
2   Unix SMB/CIFS implementation.
3
4   RFC2478 Compliant SPNEGO implementation
5
6   Copyright (C) Jim McDonough <jmcd@us.ibm.com>   2003
7
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.
12
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.
17
18
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.
22*/
23
24#include "includes.h"
25
26#undef DBGC_CLASS
27#define DBGC_CLASS DBGC_AUTH
28
29static BOOL read_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token)
30{
31	ZERO_STRUCTP(token);
32
33	asn1_start_tag(asn1, ASN1_CONTEXT(0));
34	asn1_start_tag(asn1, ASN1_SEQUENCE(0));
35
36	while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
37		int i;
38
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));
44
45			token->mechTypes = SMB_MALLOC_P(const char *);
46			for (i = 0; !asn1->has_error &&
47				     0 < asn1_tag_remaining(asn1); i++) {
48				char *p_oid = NULL;
49				token->mechTypes =
50					SMB_REALLOC_ARRAY(token->mechTypes, const char *, i + 2);
51				if (!token->mechTypes) {
52					asn1->has_error = True;
53					return False;
54				}
55				asn1_read_OID(asn1, &p_oid);
56				token->mechTypes[i] = p_oid;
57			}
58			token->mechTypes[i] = NULL;
59
60			asn1_end_tag(asn1);
61			asn1_end_tag(asn1);
62			break;
63		/* Read reqFlags */
64		case ASN1_CONTEXT(1):
65			asn1_start_tag(asn1, ASN1_CONTEXT(1));
66			asn1_read_Integer(asn1, &token->reqFlags);
67			token->reqFlags |= SPNEGO_REQ_FLAG;
68			asn1_end_tag(asn1);
69			break;
70                /* Read mechToken */
71		case ASN1_CONTEXT(2):
72			asn1_start_tag(asn1, ASN1_CONTEXT(2));
73			asn1_read_OctetString(asn1, &token->mechToken);
74			asn1_end_tag(asn1);
75			break;
76		/* Read mecListMIC */
77		case ASN1_CONTEXT(3):
78			asn1_start_tag(asn1, ASN1_CONTEXT(3));
79			if (asn1->data[asn1->ofs] == ASN1_OCTET_STRING) {
80				asn1_read_OctetString(asn1,
81						      &token->mechListMIC);
82			} else {
83				/* RFC 2478 says we have an Octet String here,
84				   but W2k sends something different... */
85				char *mechListMIC;
86				asn1_push_tag(asn1, ASN1_SEQUENCE(0));
87				asn1_push_tag(asn1, ASN1_CONTEXT(0));
88				asn1_read_GeneralString(asn1, &mechListMIC);
89				asn1_pop_tag(asn1);
90				asn1_pop_tag(asn1);
91
92				token->mechListMIC =
93					data_blob(mechListMIC, strlen(mechListMIC));
94				SAFE_FREE(mechListMIC);
95			}
96			asn1_end_tag(asn1);
97			break;
98		default:
99			asn1->has_error = True;
100			break;
101		}
102	}
103
104	asn1_end_tag(asn1);
105	asn1_end_tag(asn1);
106
107	return !asn1->has_error;
108}
109
110static BOOL write_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token)
111{
112	asn1_push_tag(asn1, ASN1_CONTEXT(0));
113	asn1_push_tag(asn1, ASN1_SEQUENCE(0));
114
115	/* Write mechTypes */
116	if (token->mechTypes && *token->mechTypes) {
117		int i;
118
119		asn1_push_tag(asn1, ASN1_CONTEXT(0));
120		asn1_push_tag(asn1, ASN1_SEQUENCE(0));
121		for (i = 0; token->mechTypes[i]; i++) {
122			asn1_write_OID(asn1, token->mechTypes[i]);
123		}
124		asn1_pop_tag(asn1);
125		asn1_pop_tag(asn1);
126	}
127
128	/* write reqFlags */
129	if (token->reqFlags & SPNEGO_REQ_FLAG) {
130		int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
131
132		asn1_push_tag(asn1, ASN1_CONTEXT(1));
133		asn1_write_Integer(asn1, flags);
134		asn1_pop_tag(asn1);
135	}
136
137	/* write mechToken */
138	if (token->mechToken.data) {
139		asn1_push_tag(asn1, ASN1_CONTEXT(2));
140		asn1_write_OctetString(asn1, token->mechToken.data,
141				       token->mechToken.length);
142		asn1_pop_tag(asn1);
143	}
144
145	/* write mechListMIC */
146	if (token->mechListMIC.data) {
147		asn1_push_tag(asn1, ASN1_CONTEXT(3));
148#if 0
149		/* This is what RFC 2478 says ... */
150		asn1_write_OctetString(asn1, token->mechListMIC.data,
151				       token->mechListMIC.length);
152#else
153		/* ... but unfortunately this is what Windows
154		   sends/expects */
155		asn1_push_tag(asn1, ASN1_SEQUENCE(0));
156		asn1_push_tag(asn1, ASN1_CONTEXT(0));
157		asn1_push_tag(asn1, ASN1_GENERAL_STRING);
158		asn1_write(asn1, token->mechListMIC.data,
159			   token->mechListMIC.length);
160		asn1_pop_tag(asn1);
161		asn1_pop_tag(asn1);
162		asn1_pop_tag(asn1);
163#endif
164		asn1_pop_tag(asn1);
165	}
166
167	asn1_pop_tag(asn1);
168	asn1_pop_tag(asn1);
169
170	return !asn1->has_error;
171}
172
173static BOOL read_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
174{
175	ZERO_STRUCTP(token);
176
177	asn1_start_tag(asn1, ASN1_CONTEXT(1));
178	asn1_start_tag(asn1, ASN1_SEQUENCE(0));
179
180	while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
181		switch (asn1->data[asn1->ofs]) {
182		case ASN1_CONTEXT(0):
183			asn1_start_tag(asn1, ASN1_CONTEXT(0));
184			asn1_start_tag(asn1, ASN1_ENUMERATED);
185			asn1_read_uint8(asn1, &token->negResult);
186			asn1_end_tag(asn1);
187			asn1_end_tag(asn1);
188			break;
189		case ASN1_CONTEXT(1):
190			asn1_start_tag(asn1, ASN1_CONTEXT(1));
191			asn1_read_OID(asn1, &token->supportedMech);
192			asn1_end_tag(asn1);
193			break;
194		case ASN1_CONTEXT(2):
195			asn1_start_tag(asn1, ASN1_CONTEXT(2));
196			asn1_read_OctetString(asn1, &token->responseToken);
197			asn1_end_tag(asn1);
198			break;
199		case ASN1_CONTEXT(3):
200			asn1_start_tag(asn1, ASN1_CONTEXT(3));
201			asn1_read_OctetString(asn1, &token->mechListMIC);
202			asn1_end_tag(asn1);
203			break;
204		default:
205			asn1->has_error = True;
206			break;
207		}
208	}
209
210	asn1_end_tag(asn1);
211	asn1_end_tag(asn1);
212
213	return !asn1->has_error;
214}
215
216static BOOL write_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
217{
218	asn1_push_tag(asn1, ASN1_CONTEXT(1));
219	asn1_push_tag(asn1, ASN1_SEQUENCE(0));
220
221	asn1_push_tag(asn1, ASN1_CONTEXT(0));
222	asn1_write_enumerated(asn1, token->negResult);
223	asn1_pop_tag(asn1);
224
225	if (token->supportedMech) {
226		asn1_push_tag(asn1, ASN1_CONTEXT(1));
227		asn1_write_OID(asn1, token->supportedMech);
228		asn1_pop_tag(asn1);
229	}
230
231	if (token->responseToken.data) {
232		asn1_push_tag(asn1, ASN1_CONTEXT(2));
233		asn1_write_OctetString(asn1, token->responseToken.data,
234				       token->responseToken.length);
235		asn1_pop_tag(asn1);
236	}
237
238	if (token->mechListMIC.data) {
239		asn1_push_tag(asn1, ASN1_CONTEXT(3));
240		asn1_write_OctetString(asn1, token->mechListMIC.data,
241				      token->mechListMIC.length);
242		asn1_pop_tag(asn1);
243	}
244
245	asn1_pop_tag(asn1);
246	asn1_pop_tag(asn1);
247
248	return !asn1->has_error;
249}
250
251ssize_t read_spnego_data(DATA_BLOB data, SPNEGO_DATA *token)
252{
253	ASN1_DATA asn1;
254	ssize_t ret = -1;
255
256	ZERO_STRUCTP(token);
257	ZERO_STRUCT(asn1);
258	asn1_load(&asn1, data);
259
260	switch (asn1.data[asn1.ofs]) {
261	case ASN1_APPLICATION(0):
262		asn1_start_tag(&asn1, ASN1_APPLICATION(0));
263		asn1_check_OID(&asn1, OID_SPNEGO);
264		if (read_negTokenInit(&asn1, &token->negTokenInit)) {
265			token->type = SPNEGO_NEG_TOKEN_INIT;
266		}
267		asn1_end_tag(&asn1);
268		break;
269	case ASN1_CONTEXT(1):
270		if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
271			token->type = SPNEGO_NEG_TOKEN_TARG;
272		}
273		break;
274	default:
275		break;
276	}
277
278	if (!asn1.has_error) ret = asn1.ofs;
279	asn1_free(&asn1);
280
281	return ret;
282}
283
284ssize_t write_spnego_data(DATA_BLOB *blob, SPNEGO_DATA *spnego)
285{
286	ASN1_DATA asn1;
287	ssize_t ret = -1;
288
289	ZERO_STRUCT(asn1);
290
291	switch (spnego->type) {
292	case SPNEGO_NEG_TOKEN_INIT:
293		asn1_push_tag(&asn1, ASN1_APPLICATION(0));
294		asn1_write_OID(&asn1, OID_SPNEGO);
295		write_negTokenInit(&asn1, &spnego->negTokenInit);
296		asn1_pop_tag(&asn1);
297		break;
298	case SPNEGO_NEG_TOKEN_TARG:
299		write_negTokenTarg(&asn1, &spnego->negTokenTarg);
300		break;
301	default:
302		asn1.has_error = True;
303		break;
304	}
305
306	if (!asn1.has_error) {
307		*blob = data_blob(asn1.data, asn1.length);
308		ret = asn1.ofs;
309	}
310	asn1_free(&asn1);
311
312	return ret;
313}
314
315BOOL free_spnego_data(SPNEGO_DATA *spnego)
316{
317	BOOL ret = True;
318
319	if (!spnego) goto out;
320
321	switch(spnego->type) {
322	case SPNEGO_NEG_TOKEN_INIT:
323		if (spnego->negTokenInit.mechTypes) {
324			int i;
325			for (i = 0; spnego->negTokenInit.mechTypes[i]; i++) {
326				free(CONST_DISCARD(char *,spnego->negTokenInit.mechTypes[i]));
327			}
328			free(spnego->negTokenInit.mechTypes);
329		}
330		data_blob_free(&spnego->negTokenInit.mechToken);
331		data_blob_free(&spnego->negTokenInit.mechListMIC);
332		break;
333	case SPNEGO_NEG_TOKEN_TARG:
334		if (spnego->negTokenTarg.supportedMech) {
335			free(spnego->negTokenTarg.supportedMech);
336		}
337		data_blob_free(&spnego->negTokenTarg.responseToken);
338		data_blob_free(&spnego->negTokenTarg.mechListMIC);
339		break;
340	default:
341		ret = False;
342		break;
343	}
344	ZERO_STRUCTP(spnego);
345out:
346	return ret;
347}
348