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