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