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
) {