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 3 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, see <http://www.gnu.org/licenses/>.
21*/
22
23#include "includes.h"
24#include "../libcli/auth/spnego.h"
25#include "../lib/util/asn1.h"
26
27static bool read_negTokenInit(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
28			      struct spnego_negTokenInit *token)
29{
30	ZERO_STRUCTP(token);
31
32	asn1_start_tag(asn1, ASN1_CONTEXT(0));
33	asn1_start_tag(asn1, ASN1_SEQUENCE(0));
34
35	while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
36		int i;
37		uint8_t context;
38		if (!asn1_peek_uint8(asn1, &context)) {
39			asn1->has_error = true;
40			break;
41		}
42
43		switch (context) {
44		/* Read mechTypes */
45		case ASN1_CONTEXT(0):
46			asn1_start_tag(asn1, ASN1_CONTEXT(0));
47			asn1_start_tag(asn1, ASN1_SEQUENCE(0));
48
49			token->mechTypes = talloc(NULL, const char *);
50			for (i = 0; !asn1->has_error &&
51				     0 < asn1_tag_remaining(asn1); i++) {
52				token->mechTypes = talloc_realloc(NULL,
53								  token->mechTypes,
54								  const char *, i+2);
55				asn1_read_OID(asn1, token->mechTypes, token->mechTypes + i);
56			}
57			token->mechTypes[i] = NULL;
58
59			asn1_end_tag(asn1);
60			asn1_end_tag(asn1);
61			break;
62		/* Read reqFlags */
63		case ASN1_CONTEXT(1):
64			asn1_start_tag(asn1, ASN1_CONTEXT(1));
65			asn1_read_BitString(asn1, mem_ctx, &token->reqFlags,
66					    &token->reqFlagsPadding);
67			asn1_end_tag(asn1);
68			break;
69                /* Read mechToken */
70		case ASN1_CONTEXT(2):
71			asn1_start_tag(asn1, ASN1_CONTEXT(2));
72			asn1_read_OctetString(asn1, mem_ctx, &token->mechToken);
73			asn1_end_tag(asn1);
74			break;
75		/* Read mecListMIC */
76		case ASN1_CONTEXT(3):
77		{
78			uint8_t type_peek;
79			asn1_start_tag(asn1, ASN1_CONTEXT(3));
80			if (!asn1_peek_uint8(asn1, &type_peek)) {
81				asn1->has_error = true;
82				break;
83			}
84			if (type_peek == ASN1_OCTET_STRING) {
85				asn1_read_OctetString(asn1, mem_ctx,
86						      &token->mechListMIC);
87			} else {
88				/* RFC 2478 says we have an Octet String here,
89				   but W2k sends something different... */
90				char *mechListMIC;
91				asn1_start_tag(asn1, ASN1_SEQUENCE(0));
92				asn1_start_tag(asn1, ASN1_CONTEXT(0));
93				asn1_read_GeneralString(asn1, mem_ctx, &mechListMIC);
94				asn1_end_tag(asn1);
95				asn1_end_tag(asn1);
96
97				token->targetPrincipal = mechListMIC;
98			}
99			asn1_end_tag(asn1);
100			break;
101		}
102		default:
103			asn1->has_error = true;
104			break;
105		}
106	}
107
108	asn1_end_tag(asn1);
109	asn1_end_tag(asn1);
110
111	return !asn1->has_error;
112}
113
114static bool write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
115{
116	asn1_push_tag(asn1, ASN1_CONTEXT(0));
117	asn1_push_tag(asn1, ASN1_SEQUENCE(0));
118
119	/* Write mechTypes */
120	if (token->mechTypes && *token->mechTypes) {
121		int i;
122
123		asn1_push_tag(asn1, ASN1_CONTEXT(0));
124		asn1_push_tag(asn1, ASN1_SEQUENCE(0));
125		for (i = 0; token->mechTypes[i]; i++) {
126			asn1_write_OID(asn1, token->mechTypes[i]);
127		}
128		asn1_pop_tag(asn1);
129		asn1_pop_tag(asn1);
130	}
131
132	/* write reqFlags */
133	if (token->reqFlags.length > 0) {
134		asn1_push_tag(asn1, ASN1_CONTEXT(1));
135		asn1_write_BitString(asn1, token->reqFlags.data,
136				     token->reqFlags.length,
137				     token->reqFlagsPadding);
138		asn1_pop_tag(asn1);
139	}
140
141	/* write mechToken */
142	if (token->mechToken.data) {
143		asn1_push_tag(asn1, ASN1_CONTEXT(2));
144		asn1_write_OctetString(asn1, token->mechToken.data,
145				       token->mechToken.length);
146		asn1_pop_tag(asn1);
147	}
148
149	/* write mechListMIC */
150	if (token->mechListMIC.data) {
151		asn1_push_tag(asn1, ASN1_CONTEXT(3));
152#if 0
153		/* This is what RFC 2478 says ... */
154		asn1_write_OctetString(asn1, token->mechListMIC.data,
155				       token->mechListMIC.length);
156#else
157		/* ... but unfortunately this is what Windows
158		   sends/expects */
159		asn1_push_tag(asn1, ASN1_SEQUENCE(0));
160		asn1_push_tag(asn1, ASN1_CONTEXT(0));
161		asn1_push_tag(asn1, ASN1_GENERAL_STRING);
162		asn1_write(asn1, token->mechListMIC.data,
163			   token->mechListMIC.length);
164		asn1_pop_tag(asn1);
165		asn1_pop_tag(asn1);
166		asn1_pop_tag(asn1);
167#endif
168		asn1_pop_tag(asn1);
169	}
170
171	asn1_pop_tag(asn1);
172	asn1_pop_tag(asn1);
173
174	return !asn1->has_error;
175}
176
177static bool read_negTokenTarg(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
178			      struct spnego_negTokenTarg *token)
179{
180	ZERO_STRUCTP(token);
181
182	asn1_start_tag(asn1, ASN1_CONTEXT(1));
183	asn1_start_tag(asn1, ASN1_SEQUENCE(0));
184
185	while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
186		uint8_t context;
187		if (!asn1_peek_uint8(asn1, &context)) {
188			asn1->has_error = true;
189			break;
190		}
191
192		switch (context) {
193		case ASN1_CONTEXT(0):
194			asn1_start_tag(asn1, ASN1_CONTEXT(0));
195			asn1_start_tag(asn1, ASN1_ENUMERATED);
196			asn1_read_uint8(asn1, &token->negResult);
197			asn1_end_tag(asn1);
198			asn1_end_tag(asn1);
199			break;
200		case ASN1_CONTEXT(1):
201			asn1_start_tag(asn1, ASN1_CONTEXT(1));
202			asn1_read_OID(asn1, mem_ctx, &token->supportedMech);
203			asn1_end_tag(asn1);
204			break;
205		case ASN1_CONTEXT(2):
206			asn1_start_tag(asn1, ASN1_CONTEXT(2));
207			asn1_read_OctetString(asn1, mem_ctx, &token->responseToken);
208			asn1_end_tag(asn1);
209			break;
210		case ASN1_CONTEXT(3):
211			asn1_start_tag(asn1, ASN1_CONTEXT(3));
212			asn1_read_OctetString(asn1, mem_ctx, &token->mechListMIC);
213			asn1_end_tag(asn1);
214			break;
215		default:
216			asn1->has_error = true;
217			break;
218		}
219	}
220
221	asn1_end_tag(asn1);
222	asn1_end_tag(asn1);
223
224	return !asn1->has_error;
225}
226
227static bool write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
228{
229	asn1_push_tag(asn1, ASN1_CONTEXT(1));
230	asn1_push_tag(asn1, ASN1_SEQUENCE(0));
231
232	if (token->negResult != SPNEGO_NONE_RESULT) {
233		asn1_push_tag(asn1, ASN1_CONTEXT(0));
234		asn1_write_enumerated(asn1, token->negResult);
235		asn1_pop_tag(asn1);
236	}
237
238	if (token->supportedMech) {
239		asn1_push_tag(asn1, ASN1_CONTEXT(1));
240		asn1_write_OID(asn1, token->supportedMech);
241		asn1_pop_tag(asn1);
242	}
243
244	if (token->responseToken.data) {
245		asn1_push_tag(asn1, ASN1_CONTEXT(2));
246		asn1_write_OctetString(asn1, token->responseToken.data,
247				       token->responseToken.length);
248		asn1_pop_tag(asn1);
249	}
250
251	if (token->mechListMIC.data) {
252		asn1_push_tag(asn1, ASN1_CONTEXT(3));
253		asn1_write_OctetString(asn1, token->mechListMIC.data,
254				      token->mechListMIC.length);
255		asn1_pop_tag(asn1);
256	}
257
258	asn1_pop_tag(asn1);
259	asn1_pop_tag(asn1);
260
261	return !asn1->has_error;
262}
263
264ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data *token)
265{
266	struct asn1_data *asn1;
267	ssize_t ret = -1;
268	uint8_t context;
269
270	ZERO_STRUCTP(token);
271
272	if (data.length == 0) {
273		return ret;
274	}
275
276	asn1 = asn1_init(mem_ctx);
277	if (asn1 == NULL) {
278		return -1;
279	}
280
281	asn1_load(asn1, data);
282
283	if (!asn1_peek_uint8(asn1, &context)) {
284		asn1->has_error = true;
285	} else {
286		switch (context) {
287		case ASN1_APPLICATION(0):
288			asn1_start_tag(asn1, ASN1_APPLICATION(0));
289			asn1_check_OID(asn1, OID_SPNEGO);
290			if (read_negTokenInit(asn1, mem_ctx, &token->negTokenInit)) {
291				token->type = SPNEGO_NEG_TOKEN_INIT;
292			}
293			asn1_end_tag(asn1);
294			break;
295		case ASN1_CONTEXT(1):
296			if (read_negTokenTarg(asn1, mem_ctx, &token->negTokenTarg)) {
297				token->type = SPNEGO_NEG_TOKEN_TARG;
298			}
299			break;
300		default:
301			asn1->has_error = true;
302			break;
303		}
304	}
305
306	if (!asn1->has_error) ret = asn1->ofs;
307	asn1_free(asn1);
308
309	return ret;
310}
311
312ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
313{
314	struct asn1_data *asn1 = asn1_init(mem_ctx);
315	ssize_t ret = -1;
316
317	if (asn1 == NULL) {
318		return -1;
319	}
320
321	switch (spnego->type) {
322	case SPNEGO_NEG_TOKEN_INIT:
323		asn1_push_tag(asn1, ASN1_APPLICATION(0));
324		asn1_write_OID(asn1, OID_SPNEGO);
325		write_negTokenInit(asn1, &spnego->negTokenInit);
326		asn1_pop_tag(asn1);
327		break;
328	case SPNEGO_NEG_TOKEN_TARG:
329		write_negTokenTarg(asn1, &spnego->negTokenTarg);
330		break;
331	default:
332		asn1->has_error = true;
333		break;
334	}
335
336	if (!asn1->has_error) {
337		*blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
338		ret = asn1->ofs;
339	}
340	asn1_free(asn1);
341
342	return ret;
343}
344
345bool spnego_free_data(struct spnego_data *spnego)
346{
347	bool ret = true;
348
349	if (!spnego) goto out;
350
351	switch(spnego->type) {
352	case SPNEGO_NEG_TOKEN_INIT:
353		if (spnego->negTokenInit.mechTypes) {
354			talloc_free(spnego->negTokenInit.mechTypes);
355		}
356		data_blob_free(&spnego->negTokenInit.reqFlags);
357		data_blob_free(&spnego->negTokenInit.mechToken);
358		data_blob_free(&spnego->negTokenInit.mechListMIC);
359		talloc_free(spnego->negTokenInit.targetPrincipal);
360		break;
361	case SPNEGO_NEG_TOKEN_TARG:
362		if (spnego->negTokenTarg.supportedMech) {
363			talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
364		}
365		data_blob_free(&spnego->negTokenTarg.responseToken);
366		data_blob_free(&spnego->negTokenTarg.mechListMIC);
367		break;
368	default:
369		ret = false;
370		break;
371	}
372	ZERO_STRUCTP(spnego);
373out:
374	return ret;
375}
376
377bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
378			     const char **mech_types,
379			     DATA_BLOB *blob)
380{
381	struct asn1_data *asn1 = asn1_init(mem_ctx);
382
383	/* Write mechTypes */
384	if (mech_types && *mech_types) {
385		int i;
386
387		asn1_push_tag(asn1, ASN1_SEQUENCE(0));
388		for (i = 0; mech_types[i]; i++) {
389			asn1_write_OID(asn1, mech_types[i]);
390		}
391		asn1_pop_tag(asn1);
392	}
393
394	if (asn1->has_error) {
395		asn1_free(asn1);
396		return false;
397	}
398
399	*blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
400	if (blob->length != asn1->length) {
401		asn1_free(asn1);
402		return false;
403	}
404
405	asn1_free(asn1);
406
407	return true;
408}
409