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 3 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, see <http://www.gnu.org/licenses/>.
24 #include "../libcli/auth/spnego.h"
25 #include "../lib/util/asn1.h"
27 static bool read_negTokenInit(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
28 struct spnego_negTokenInit *token)
32 asn1_start_tag(asn1, ASN1_CONTEXT(0));
33 asn1_start_tag(asn1, ASN1_SEQUENCE(0));
35 while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
38 if (!asn1_peek_uint8(asn1, &context)) {
39 asn1->has_error = true;
45 case ASN1_CONTEXT(0): {
46 const char **mechTypes;
48 asn1_start_tag(asn1, ASN1_CONTEXT(0));
49 asn1_start_tag(asn1, ASN1_SEQUENCE(0));
51 mechTypes = talloc(mem_ctx, const char *);
52 if (mechTypes == NULL) {
53 asn1->has_error = true;
56 for (i = 0; !asn1->has_error &&
57 0 < asn1_tag_remaining(asn1); i++) {
60 p = talloc_realloc(mem_ctx,
64 talloc_free(mechTypes);
65 asn1->has_error = true;
70 asn1_read_OID(asn1, mechTypes, &oid);
74 token->mechTypes = mechTypes;
82 asn1_start_tag(asn1, ASN1_CONTEXT(1));
83 asn1_read_BitString(asn1, mem_ctx, &token->reqFlags,
84 &token->reqFlagsPadding);
89 asn1_start_tag(asn1, ASN1_CONTEXT(2));
90 asn1_read_OctetString(asn1, mem_ctx, &token->mechToken);
97 asn1_start_tag(asn1, ASN1_CONTEXT(3));
98 if (!asn1_peek_uint8(asn1, &type_peek)) {
99 asn1->has_error = true;
102 if (type_peek == ASN1_OCTET_STRING) {
103 asn1_read_OctetString(asn1, mem_ctx,
104 &token->mechListMIC);
106 /* RFC 2478 says we have an Octet String here,
107 but W2k sends something different... */
109 asn1_start_tag(asn1, ASN1_SEQUENCE(0));
110 asn1_start_tag(asn1, ASN1_CONTEXT(0));
111 asn1_read_GeneralString(asn1, mem_ctx, &mechListMIC);
115 token->targetPrincipal = mechListMIC;
121 asn1->has_error = true;
129 return !asn1->has_error;
132 static bool write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
134 asn1_push_tag(asn1, ASN1_CONTEXT(0));
135 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
137 /* Write mechTypes */
138 if (token->mechTypes && *token->mechTypes) {
141 asn1_push_tag(asn1, ASN1_CONTEXT(0));
142 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
143 for (i = 0; token->mechTypes[i]; i++) {
144 asn1_write_OID(asn1, token->mechTypes[i]);
151 if (token->reqFlags.length > 0) {
152 asn1_push_tag(asn1, ASN1_CONTEXT(1));
153 asn1_write_BitString(asn1, token->reqFlags.data,
154 token->reqFlags.length,
155 token->reqFlagsPadding);
159 /* write mechToken */
160 if (token->mechToken.data) {
161 asn1_push_tag(asn1, ASN1_CONTEXT(2));
162 asn1_write_OctetString(asn1, token->mechToken.data,
163 token->mechToken.length);
167 /* write mechListMIC */
168 if (token->mechListMIC.data) {
169 asn1_push_tag(asn1, ASN1_CONTEXT(3));
171 /* This is what RFC 2478 says ... */
172 asn1_write_OctetString(asn1, token->mechListMIC.data,
173 token->mechListMIC.length);
175 /* ... but unfortunately this is what Windows
177 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
178 asn1_push_tag(asn1, ASN1_CONTEXT(0));
179 asn1_push_tag(asn1, ASN1_GENERAL_STRING);
180 asn1_write(asn1, token->mechListMIC.data,
181 token->mechListMIC.length);
192 return !asn1->has_error;
195 static bool read_negTokenTarg(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
196 struct spnego_negTokenTarg *token)
200 asn1_start_tag(asn1, ASN1_CONTEXT(1));
201 asn1_start_tag(asn1, ASN1_SEQUENCE(0));
203 while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
206 if (!asn1_peek_uint8(asn1, &context)) {
207 asn1->has_error = true;
212 case ASN1_CONTEXT(0):
213 asn1_start_tag(asn1, ASN1_CONTEXT(0));
214 asn1_start_tag(asn1, ASN1_ENUMERATED);
215 asn1_read_uint8(asn1, &token->negResult);
219 case ASN1_CONTEXT(1):
220 asn1_start_tag(asn1, ASN1_CONTEXT(1));
221 asn1_read_OID(asn1, mem_ctx, &oid);
222 token->supportedMech = oid;
225 case ASN1_CONTEXT(2):
226 asn1_start_tag(asn1, ASN1_CONTEXT(2));
227 asn1_read_OctetString(asn1, mem_ctx, &token->responseToken);
230 case ASN1_CONTEXT(3):
231 asn1_start_tag(asn1, ASN1_CONTEXT(3));
232 asn1_read_OctetString(asn1, mem_ctx, &token->mechListMIC);
236 asn1->has_error = true;
244 return !asn1->has_error;
247 static bool write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
249 asn1_push_tag(asn1, ASN1_CONTEXT(1));
250 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
252 if (token->negResult != SPNEGO_NONE_RESULT) {
253 asn1_push_tag(asn1, ASN1_CONTEXT(0));
254 asn1_write_enumerated(asn1, token->negResult);
258 if (token->supportedMech) {
259 asn1_push_tag(asn1, ASN1_CONTEXT(1));
260 asn1_write_OID(asn1, token->supportedMech);
264 if (token->responseToken.data) {
265 asn1_push_tag(asn1, ASN1_CONTEXT(2));
266 asn1_write_OctetString(asn1, token->responseToken.data,
267 token->responseToken.length);
271 if (token->mechListMIC.data) {
272 asn1_push_tag(asn1, ASN1_CONTEXT(3));
273 asn1_write_OctetString(asn1, token->mechListMIC.data,
274 token->mechListMIC.length);
281 return !asn1->has_error;
284 ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data *token)
286 struct asn1_data *asn1;
292 if (data.length == 0) {
296 asn1 = asn1_init(mem_ctx);
301 asn1_load(asn1, data);
303 if (!asn1_peek_uint8(asn1, &context)) {
304 asn1->has_error = true;
307 case ASN1_APPLICATION(0):
308 asn1_start_tag(asn1, ASN1_APPLICATION(0));
309 asn1_check_OID(asn1, OID_SPNEGO);
310 if (read_negTokenInit(asn1, mem_ctx, &token->negTokenInit)) {
311 token->type = SPNEGO_NEG_TOKEN_INIT;
315 case ASN1_CONTEXT(1):
316 if (read_negTokenTarg(asn1, mem_ctx, &token->negTokenTarg)) {
317 token->type = SPNEGO_NEG_TOKEN_TARG;
321 asn1->has_error = true;
326 if (!asn1->has_error) ret = asn1->ofs;
332 ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
334 struct asn1_data *asn1 = asn1_init(mem_ctx);
341 switch (spnego->type) {
342 case SPNEGO_NEG_TOKEN_INIT:
343 asn1_push_tag(asn1, ASN1_APPLICATION(0));
344 asn1_write_OID(asn1, OID_SPNEGO);
345 write_negTokenInit(asn1, &spnego->negTokenInit);
348 case SPNEGO_NEG_TOKEN_TARG:
349 write_negTokenTarg(asn1, &spnego->negTokenTarg);
352 asn1->has_error = true;
356 if (!asn1->has_error) {
357 *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
365 bool spnego_free_data(struct spnego_data *spnego)
369 if (!spnego) goto out;
371 switch(spnego->type) {
372 case SPNEGO_NEG_TOKEN_INIT:
373 if (spnego->negTokenInit.mechTypes) {
374 talloc_free(discard_const(spnego->negTokenInit.mechTypes));
376 data_blob_free(&spnego->negTokenInit.reqFlags);
377 data_blob_free(&spnego->negTokenInit.mechToken);
378 data_blob_free(&spnego->negTokenInit.mechListMIC);
379 talloc_free(spnego->negTokenInit.targetPrincipal);
381 case SPNEGO_NEG_TOKEN_TARG:
382 if (spnego->negTokenTarg.supportedMech) {
383 talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
385 data_blob_free(&spnego->negTokenTarg.responseToken);
386 data_blob_free(&spnego->negTokenTarg.mechListMIC);
392 ZERO_STRUCTP(spnego);
397 bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
398 const char * const *mech_types,
401 struct asn1_data *asn1 = asn1_init(mem_ctx);
407 /* Write mechTypes */
408 if (mech_types && *mech_types) {
411 asn1_push_tag(asn1, ASN1_SEQUENCE(0));
412 for (i = 0; mech_types[i]; i++) {
413 asn1_write_OID(asn1, mech_types[i]);
418 if (asn1->has_error) {
423 *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
424 if (blob->length != asn1->length) {