Merge pull request #203 from sdigit/patch-1
[heimdal.git] / lib / krb5 / digest.c
blob7be249253bdbc659def47482478f07fdb78b3429
1 /*
2 * Copyright (c) 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "krb5_locl.h"
35 #include "digest_asn1.h"
37 #ifndef HEIMDAL_SMALLER
39 struct krb5_digest_data {
40 char *cbtype;
41 char *cbbinding;
43 DigestInit init;
44 DigestInitReply initReply;
45 DigestRequest request;
46 DigestResponse response;
49 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
50 krb5_digest_alloc(krb5_context context, krb5_digest *digest)
52 krb5_digest d;
54 d = calloc(1, sizeof(*d));
55 if (d == NULL) {
56 *digest = NULL;
57 return krb5_enomem(context);
59 *digest = d;
61 return 0;
64 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
65 krb5_digest_free(krb5_digest digest)
67 if (digest == NULL)
68 return;
69 free_DigestInit(&digest->init);
70 free_DigestInitReply(&digest->initReply);
71 free_DigestRequest(&digest->request);
72 free_DigestResponse(&digest->response);
73 memset(digest, 0, sizeof(*digest));
74 free(digest);
75 return;
78 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
79 krb5_digest_set_server_cb(krb5_context context,
80 krb5_digest digest,
81 const char *type,
82 const char *binding)
84 if (digest->init.channel) {
85 krb5_set_error_message(context, EINVAL,
86 N_("server channel binding already set", ""));
87 return EINVAL;
89 digest->init.channel = calloc(1, sizeof(*digest->init.channel));
90 if (digest->init.channel == NULL)
91 goto error;
93 digest->init.channel->cb_type = strdup(type);
94 if (digest->init.channel->cb_type == NULL)
95 goto error;
97 digest->init.channel->cb_binding = strdup(binding);
98 if (digest->init.channel->cb_binding == NULL)
99 goto error;
100 return 0;
101 error:
102 if (digest->init.channel) {
103 free(digest->init.channel->cb_type);
104 free(digest->init.channel->cb_binding);
105 free(digest->init.channel);
106 digest->init.channel = NULL;
108 return krb5_enomem(context);
111 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
112 krb5_digest_set_type(krb5_context context,
113 krb5_digest digest,
114 const char *type)
116 if (digest->init.type) {
117 krb5_set_error_message(context, EINVAL, "client type already set");
118 return EINVAL;
120 digest->init.type = strdup(type);
121 if (digest->init.type == NULL)
122 return krb5_enomem(context);
123 return 0;
126 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
127 krb5_digest_set_hostname(krb5_context context,
128 krb5_digest digest,
129 const char *hostname)
131 if (digest->init.hostname) {
132 krb5_set_error_message(context, EINVAL, "server hostname already set");
133 return EINVAL;
135 digest->init.hostname = malloc(sizeof(*digest->init.hostname));
136 if (digest->init.hostname == NULL)
137 return krb5_enomem(context);
138 *digest->init.hostname = strdup(hostname);
139 if (*digest->init.hostname == NULL) {
140 free(digest->init.hostname);
141 digest->init.hostname = NULL;
142 return krb5_enomem(context);
144 return 0;
147 KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
148 krb5_digest_get_server_nonce(krb5_context context,
149 krb5_digest digest)
151 return digest->initReply.nonce;
154 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
155 krb5_digest_set_server_nonce(krb5_context context,
156 krb5_digest digest,
157 const char *nonce)
159 if (digest->request.serverNonce) {
160 krb5_set_error_message(context, EINVAL, N_("nonce already set", ""));
161 return EINVAL;
163 digest->request.serverNonce = strdup(nonce);
164 if (digest->request.serverNonce == NULL)
165 return krb5_enomem(context);
166 return 0;
169 KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
170 krb5_digest_get_opaque(krb5_context context,
171 krb5_digest digest)
173 return digest->initReply.opaque;
176 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
177 krb5_digest_set_opaque(krb5_context context,
178 krb5_digest digest,
179 const char *opaque)
181 if (digest->request.opaque) {
182 krb5_set_error_message(context, EINVAL, "opaque already set");
183 return EINVAL;
185 digest->request.opaque = strdup(opaque);
186 if (digest->request.opaque == NULL)
187 return krb5_enomem(context);
188 return 0;
191 KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
192 krb5_digest_get_identifier(krb5_context context,
193 krb5_digest digest)
195 if (digest->initReply.identifier == NULL)
196 return NULL;
197 return *digest->initReply.identifier;
200 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
201 krb5_digest_set_identifier(krb5_context context,
202 krb5_digest digest,
203 const char *id)
205 if (digest->request.identifier) {
206 krb5_set_error_message(context, EINVAL, N_("identifier already set", ""));
207 return EINVAL;
209 digest->request.identifier = calloc(1, sizeof(*digest->request.identifier));
210 if (digest->request.identifier == NULL)
211 return krb5_enomem(context);
212 *digest->request.identifier = strdup(id);
213 if (*digest->request.identifier == NULL) {
214 free(digest->request.identifier);
215 digest->request.identifier = NULL;
216 return krb5_enomem(context);
218 return 0;
221 static krb5_error_code
222 digest_request(krb5_context context,
223 krb5_realm realm,
224 krb5_ccache ccache,
225 krb5_key_usage usage,
226 const DigestReqInner *ireq,
227 DigestRepInner *irep)
229 DigestREQ req;
230 DigestREP rep;
231 krb5_error_code ret;
232 krb5_data data, data2;
233 size_t size = 0;
234 krb5_crypto crypto = NULL;
235 krb5_auth_context ac = NULL;
236 krb5_principal principal = NULL;
237 krb5_ccache id = NULL;
238 krb5_realm r = NULL;
240 krb5_data_zero(&data);
241 krb5_data_zero(&data2);
242 memset(&req, 0, sizeof(req));
243 memset(&rep, 0, sizeof(rep));
245 if (ccache == NULL) {
246 ret = krb5_cc_default(context, &id);
247 if (ret)
248 goto out;
249 } else
250 id = ccache;
252 if (realm == NULL) {
253 ret = krb5_get_default_realm(context, &r);
254 if (ret)
255 goto out;
256 } else
257 r = realm;
263 ret = krb5_make_principal(context, &principal,
264 r, KRB5_DIGEST_NAME, r, NULL);
265 if (ret)
266 goto out;
268 ASN1_MALLOC_ENCODE(DigestReqInner, data.data, data.length,
269 ireq, &size, ret);
270 if (ret) {
271 krb5_set_error_message(context, ret,
272 N_("Failed to encode digest inner request", ""));
273 goto out;
275 if (size != data.length)
276 krb5_abortx(context, "ASN.1 internal encoder error");
278 ret = krb5_mk_req_exact(context, &ac,
279 AP_OPTS_USE_SUBKEY|AP_OPTS_MUTUAL_REQUIRED,
280 principal, NULL, id, &req.apReq);
281 if (ret)
282 goto out;
285 krb5_keyblock *key;
287 ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
288 if (ret)
289 goto out;
290 if (key == NULL) {
291 ret = EINVAL;
292 krb5_set_error_message(context, ret,
293 N_("Digest failed to get local subkey", ""));
294 goto out;
297 ret = krb5_crypto_init(context, key, 0, &crypto);
298 krb5_free_keyblock (context, key);
299 if (ret)
300 goto out;
303 ret = krb5_encrypt_EncryptedData(context, crypto, usage,
304 data.data, data.length, 0,
305 &req.innerReq);
306 if (ret)
307 goto out;
309 krb5_data_free(&data);
311 ASN1_MALLOC_ENCODE(DigestREQ, data.data, data.length,
312 &req, &size, ret);
313 if (ret) {
314 krb5_set_error_message(context, ret,
315 N_("Failed to encode DigestREQest", ""));
316 goto out;
318 if (size != data.length)
319 krb5_abortx(context, "ASN.1 internal encoder error");
321 ret = krb5_sendto_kdc(context, &data, &r, &data2);
322 if (ret)
323 goto out;
325 ret = decode_DigestREP(data2.data, data2.length, &rep, NULL);
326 if (ret) {
327 krb5_set_error_message(context, ret,
328 N_("Failed to parse digest response", ""));
329 goto out;
333 krb5_ap_rep_enc_part *repl;
335 ret = krb5_rd_rep(context, ac, &rep.apRep, &repl);
336 if (ret)
337 goto out;
339 krb5_free_ap_rep_enc_part(context, repl);
342 krb5_keyblock *key;
344 ret = krb5_auth_con_getremotesubkey(context, ac, &key);
345 if (ret)
346 goto out;
347 if (key == NULL) {
348 ret = EINVAL;
349 krb5_set_error_message(context, ret,
350 N_("Digest reply have no remote subkey", ""));
351 goto out;
354 krb5_crypto_destroy(context, crypto);
355 ret = krb5_crypto_init(context, key, 0, &crypto);
356 krb5_free_keyblock (context, key);
357 if (ret)
358 goto out;
361 krb5_data_free(&data);
362 ret = krb5_decrypt_EncryptedData(context, crypto, usage,
363 &rep.innerRep, &data);
364 if (ret)
365 goto out;
367 ret = decode_DigestRepInner(data.data, data.length, irep, NULL);
368 if (ret) {
369 krb5_set_error_message(context, ret,
370 N_("Failed to decode digest inner reply", ""));
371 goto out;
374 out:
375 if (ccache == NULL && id)
376 krb5_cc_close(context, id);
377 if (realm == NULL && r)
378 free(r);
379 if (crypto)
380 krb5_crypto_destroy(context, crypto);
381 if (ac)
382 krb5_auth_con_free(context, ac);
383 if (principal)
384 krb5_free_principal(context, principal);
386 krb5_data_free(&data);
387 krb5_data_free(&data2);
389 free_DigestREQ(&req);
390 free_DigestREP(&rep);
392 return ret;
395 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
396 krb5_digest_init_request(krb5_context context,
397 krb5_digest digest,
398 krb5_realm realm,
399 krb5_ccache ccache)
401 DigestReqInner ireq;
402 DigestRepInner irep;
403 krb5_error_code ret;
405 memset(&ireq, 0, sizeof(ireq));
406 memset(&irep, 0, sizeof(irep));
408 if (digest->init.type == NULL) {
409 krb5_set_error_message(context, EINVAL,
410 N_("Type missing from init req", ""));
411 return EINVAL;
414 ireq.element = choice_DigestReqInner_init;
415 ireq.u.init = digest->init;
417 ret = digest_request(context, realm, ccache,
418 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
419 if (ret)
420 goto out;
422 if (irep.element == choice_DigestRepInner_error) {
423 ret = irep.u.error.code;
424 krb5_set_error_message(context, ret, N_("Digest init error: %s", ""),
425 irep.u.error.reason);
426 goto out;
429 if (irep.element != choice_DigestRepInner_initReply) {
430 ret = EINVAL;
431 krb5_set_error_message(context, ret,
432 N_("digest reply not an initReply", ""));
433 goto out;
436 ret = copy_DigestInitReply(&irep.u.initReply, &digest->initReply);
437 if (ret) {
438 krb5_set_error_message(context, ret,
439 N_("Failed to copy initReply", ""));
440 goto out;
443 out:
444 free_DigestRepInner(&irep);
446 return ret;
450 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
451 krb5_digest_set_client_nonce(krb5_context context,
452 krb5_digest digest,
453 const char *nonce)
455 if (digest->request.clientNonce) {
456 krb5_set_error_message(context, EINVAL,
457 N_("clientNonce already set", ""));
458 return EINVAL;
460 digest->request.clientNonce =
461 calloc(1, sizeof(*digest->request.clientNonce));
462 if (digest->request.clientNonce == NULL)
463 return krb5_enomem(context);
464 *digest->request.clientNonce = strdup(nonce);
465 if (*digest->request.clientNonce == NULL) {
466 free(digest->request.clientNonce);
467 digest->request.clientNonce = NULL;
468 return krb5_enomem(context);
470 return 0;
473 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
474 krb5_digest_set_digest(krb5_context context,
475 krb5_digest digest,
476 const char *dgst)
478 if (digest->request.digest) {
479 krb5_set_error_message(context, EINVAL,
480 N_("digest already set", ""));
481 return EINVAL;
483 digest->request.digest = strdup(dgst);
484 if (digest->request.digest == NULL)
485 return krb5_enomem(context);
486 return 0;
489 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
490 krb5_digest_set_username(krb5_context context,
491 krb5_digest digest,
492 const char *username)
494 if (digest->request.username) {
495 krb5_set_error_message(context, EINVAL, "username already set");
496 return EINVAL;
498 digest->request.username = strdup(username);
499 if (digest->request.username == NULL)
500 return krb5_enomem(context);
501 return 0;
504 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
505 krb5_digest_set_authid(krb5_context context,
506 krb5_digest digest,
507 const char *authid)
509 if (digest->request.authid) {
510 krb5_set_error_message(context, EINVAL, "authid already set");
511 return EINVAL;
513 digest->request.authid = malloc(sizeof(*digest->request.authid));
514 if (digest->request.authid == NULL)
515 return krb5_enomem(context);
516 *digest->request.authid = strdup(authid);
517 if (*digest->request.authid == NULL) {
518 free(digest->request.authid);
519 digest->request.authid = NULL;
520 return krb5_enomem(context);
522 return 0;
525 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
526 krb5_digest_set_authentication_user(krb5_context context,
527 krb5_digest digest,
528 krb5_principal authentication_user)
530 krb5_error_code ret;
532 if (digest->request.authentication_user) {
533 krb5_set_error_message(context, EINVAL,
534 N_("authentication_user already set", ""));
535 return EINVAL;
537 ret = krb5_copy_principal(context,
538 authentication_user,
539 &digest->request.authentication_user);
540 if (ret)
541 return ret;
542 return 0;
545 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
546 krb5_digest_set_realm(krb5_context context,
547 krb5_digest digest,
548 const char *realm)
550 if (digest->request.realm) {
551 krb5_set_error_message(context, EINVAL, "realm already set");
552 return EINVAL;
554 digest->request.realm = malloc(sizeof(*digest->request.realm));
555 if (digest->request.realm == NULL)
556 return krb5_enomem(context);
557 *digest->request.realm = strdup(realm);
558 if (*digest->request.realm == NULL) {
559 free(digest->request.realm);
560 digest->request.realm = NULL;
561 return krb5_enomem(context);
563 return 0;
566 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
567 krb5_digest_set_method(krb5_context context,
568 krb5_digest digest,
569 const char *method)
571 if (digest->request.method) {
572 krb5_set_error_message(context, EINVAL,
573 N_("method already set", ""));
574 return EINVAL;
576 digest->request.method = malloc(sizeof(*digest->request.method));
577 if (digest->request.method == NULL)
578 return krb5_enomem(context);
579 *digest->request.method = strdup(method);
580 if (*digest->request.method == NULL) {
581 free(digest->request.method);
582 digest->request.method = NULL;
583 return krb5_enomem(context);
585 return 0;
588 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
589 krb5_digest_set_uri(krb5_context context,
590 krb5_digest digest,
591 const char *uri)
593 if (digest->request.uri) {
594 krb5_set_error_message(context, EINVAL, N_("uri already set", ""));
595 return EINVAL;
597 digest->request.uri = malloc(sizeof(*digest->request.uri));
598 if (digest->request.uri == NULL)
599 return krb5_enomem(context);
600 *digest->request.uri = strdup(uri);
601 if (*digest->request.uri == NULL) {
602 free(digest->request.uri);
603 digest->request.uri = NULL;
604 return krb5_enomem(context);
606 return 0;
609 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
610 krb5_digest_set_nonceCount(krb5_context context,
611 krb5_digest digest,
612 const char *nonce_count)
614 if (digest->request.nonceCount) {
615 krb5_set_error_message(context, EINVAL,
616 N_("nonceCount already set", ""));
617 return EINVAL;
619 digest->request.nonceCount =
620 malloc(sizeof(*digest->request.nonceCount));
621 if (digest->request.nonceCount == NULL)
622 return krb5_enomem(context);
623 *digest->request.nonceCount = strdup(nonce_count);
624 if (*digest->request.nonceCount == NULL) {
625 free(digest->request.nonceCount);
626 digest->request.nonceCount = NULL;
627 return krb5_enomem(context);
629 return 0;
632 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
633 krb5_digest_set_qop(krb5_context context,
634 krb5_digest digest,
635 const char *qop)
637 if (digest->request.qop) {
638 krb5_set_error_message(context, EINVAL, "qop already set");
639 return EINVAL;
641 digest->request.qop = malloc(sizeof(*digest->request.qop));
642 if (digest->request.qop == NULL)
643 return krb5_enomem(context);
644 *digest->request.qop = strdup(qop);
645 if (*digest->request.qop == NULL) {
646 free(digest->request.qop);
647 digest->request.qop = NULL;
648 return krb5_enomem(context);
650 return 0;
653 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
654 krb5_digest_set_responseData(krb5_context context,
655 krb5_digest digest,
656 const char *response)
658 digest->request.responseData = strdup(response);
659 if (digest->request.responseData == NULL)
660 return krb5_enomem(context);
661 return 0;
664 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
665 krb5_digest_request(krb5_context context,
666 krb5_digest digest,
667 krb5_realm realm,
668 krb5_ccache ccache)
670 DigestReqInner ireq;
671 DigestRepInner irep;
672 krb5_error_code ret;
674 memset(&ireq, 0, sizeof(ireq));
675 memset(&irep, 0, sizeof(irep));
677 ireq.element = choice_DigestReqInner_digestRequest;
678 ireq.u.digestRequest = digest->request;
680 if (digest->request.type == NULL) {
681 if (digest->init.type == NULL) {
682 krb5_set_error_message(context, EINVAL,
683 N_("Type missing from req", ""));
684 return EINVAL;
686 ireq.u.digestRequest.type = digest->init.type;
689 if (ireq.u.digestRequest.digest == NULL) {
690 static char md5[] = "md5";
691 ireq.u.digestRequest.digest = md5;
694 ret = digest_request(context, realm, ccache,
695 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
696 if (ret)
697 return ret;
699 if (irep.element == choice_DigestRepInner_error) {
700 ret = irep.u.error.code;
701 krb5_set_error_message(context, ret,
702 N_("Digest response error: %s", ""),
703 irep.u.error.reason);
704 goto out;
707 if (irep.element != choice_DigestRepInner_response) {
708 krb5_set_error_message(context, EINVAL,
709 N_("digest reply not an DigestResponse", ""));
710 ret = EINVAL;
711 goto out;
714 ret = copy_DigestResponse(&irep.u.response, &digest->response);
715 if (ret) {
716 krb5_set_error_message(context, ret,
717 N_("Failed to copy initReply,", ""));
718 goto out;
721 out:
722 free_DigestRepInner(&irep);
724 return ret;
727 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
728 krb5_digest_rep_get_status(krb5_context context,
729 krb5_digest digest)
731 return digest->response.success ? TRUE : FALSE;
734 KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
735 krb5_digest_get_rsp(krb5_context context,
736 krb5_digest digest)
738 if (digest->response.rsp == NULL)
739 return NULL;
740 return *digest->response.rsp;
743 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
744 krb5_digest_get_tickets(krb5_context context,
745 krb5_digest digest,
746 Ticket **tickets)
748 *tickets = NULL;
749 return 0;
753 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
754 krb5_digest_get_client_binding(krb5_context context,
755 krb5_digest digest,
756 char **type,
757 char **binding)
759 if (digest->response.channel) {
760 *type = strdup(digest->response.channel->cb_type);
761 *binding = strdup(digest->response.channel->cb_binding);
762 if (*type == NULL || *binding == NULL) {
763 free(*type);
764 free(*binding);
765 return krb5_enomem(context);
767 } else {
768 *type = NULL;
769 *binding = NULL;
771 return 0;
774 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
775 krb5_digest_get_session_key(krb5_context context,
776 krb5_digest digest,
777 krb5_data *data)
779 krb5_error_code ret;
781 krb5_data_zero(data);
782 if (digest->response.session_key == NULL)
783 return 0;
784 ret = der_copy_octet_string(digest->response.session_key, data);
785 if (ret)
786 krb5_clear_error_message(context);
788 return ret;
791 struct krb5_ntlm_data {
792 NTLMInit init;
793 NTLMInitReply initReply;
794 NTLMRequest request;
795 NTLMResponse response;
798 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
799 krb5_ntlm_alloc(krb5_context context,
800 krb5_ntlm *ntlm)
802 *ntlm = calloc(1, sizeof(**ntlm));
803 if (*ntlm == NULL)
804 return krb5_enomem(context);
805 return 0;
808 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
809 krb5_ntlm_free(krb5_context context, krb5_ntlm ntlm)
811 free_NTLMInit(&ntlm->init);
812 free_NTLMInitReply(&ntlm->initReply);
813 free_NTLMRequest(&ntlm->request);
814 free_NTLMResponse(&ntlm->response);
815 memset(ntlm, 0, sizeof(*ntlm));
816 free(ntlm);
817 return 0;
821 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
822 krb5_ntlm_init_request(krb5_context context,
823 krb5_ntlm ntlm,
824 krb5_realm realm,
825 krb5_ccache ccache,
826 uint32_t flags,
827 const char *hostname,
828 const char *domainname)
830 DigestReqInner ireq;
831 DigestRepInner irep;
832 krb5_error_code ret;
834 memset(&ireq, 0, sizeof(ireq));
835 memset(&irep, 0, sizeof(irep));
837 ntlm->init.flags = flags;
838 if (hostname) {
839 ALLOC(ntlm->init.hostname, 1);
840 *ntlm->init.hostname = strdup(hostname);
842 if (domainname) {
843 ALLOC(ntlm->init.domain, 1);
844 *ntlm->init.domain = strdup(domainname);
847 ireq.element = choice_DigestReqInner_ntlmInit;
848 ireq.u.ntlmInit = ntlm->init;
850 ret = digest_request(context, realm, ccache,
851 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
852 if (ret)
853 goto out;
855 if (irep.element == choice_DigestRepInner_error) {
856 ret = irep.u.error.code;
857 krb5_set_error_message(context, ret, N_("Digest init error: %s", ""),
858 irep.u.error.reason);
859 goto out;
862 if (irep.element != choice_DigestRepInner_ntlmInitReply) {
863 ret = EINVAL;
864 krb5_set_error_message(context, ret,
865 N_("ntlm reply not an initReply", ""));
866 goto out;
869 ret = copy_NTLMInitReply(&irep.u.ntlmInitReply, &ntlm->initReply);
870 if (ret) {
871 krb5_set_error_message(context, ret,
872 N_("Failed to copy initReply", ""));
873 goto out;
876 out:
877 free_DigestRepInner(&irep);
879 return ret;
882 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
883 krb5_ntlm_init_get_flags(krb5_context context,
884 krb5_ntlm ntlm,
885 uint32_t *flags)
887 *flags = ntlm->initReply.flags;
888 return 0;
891 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
892 krb5_ntlm_init_get_challenge(krb5_context context,
893 krb5_ntlm ntlm,
894 krb5_data *challenge)
896 krb5_error_code ret;
898 ret = der_copy_octet_string(&ntlm->initReply.challenge, challenge);
899 if (ret)
900 krb5_clear_error_message(context);
902 return ret;
905 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
906 krb5_ntlm_init_get_opaque(krb5_context context,
907 krb5_ntlm ntlm,
908 krb5_data *opaque)
910 krb5_error_code ret;
912 ret = der_copy_octet_string(&ntlm->initReply.opaque, opaque);
913 if (ret)
914 krb5_clear_error_message(context);
916 return ret;
919 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
920 krb5_ntlm_init_get_targetname(krb5_context context,
921 krb5_ntlm ntlm,
922 char **name)
924 *name = strdup(ntlm->initReply.targetname);
925 if (*name == NULL)
926 return krb5_enomem(context);
927 return 0;
930 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
931 krb5_ntlm_init_get_targetinfo(krb5_context context,
932 krb5_ntlm ntlm,
933 krb5_data *data)
935 krb5_error_code ret;
937 if (ntlm->initReply.targetinfo == NULL) {
938 krb5_data_zero(data);
939 return 0;
942 ret = krb5_data_copy(data,
943 ntlm->initReply.targetinfo->data,
944 ntlm->initReply.targetinfo->length);
945 if (ret) {
946 krb5_clear_error_message(context);
947 return ret;
949 return 0;
953 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
954 krb5_ntlm_request(krb5_context context,
955 krb5_ntlm ntlm,
956 krb5_realm realm,
957 krb5_ccache ccache)
959 DigestReqInner ireq;
960 DigestRepInner irep;
961 krb5_error_code ret;
963 memset(&ireq, 0, sizeof(ireq));
964 memset(&irep, 0, sizeof(irep));
966 ireq.element = choice_DigestReqInner_ntlmRequest;
967 ireq.u.ntlmRequest = ntlm->request;
969 ret = digest_request(context, realm, ccache,
970 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
971 if (ret)
972 return ret;
974 if (irep.element == choice_DigestRepInner_error) {
975 ret = irep.u.error.code;
976 krb5_set_error_message(context, ret,
977 N_("NTLM response error: %s", ""),
978 irep.u.error.reason);
979 goto out;
982 if (irep.element != choice_DigestRepInner_ntlmResponse) {
983 ret = EINVAL;
984 krb5_set_error_message(context, ret,
985 N_("NTLM reply not an NTLMResponse", ""));
986 goto out;
989 ret = copy_NTLMResponse(&irep.u.ntlmResponse, &ntlm->response);
990 if (ret) {
991 krb5_set_error_message(context, ret,
992 N_("Failed to copy NTLMResponse", ""));
993 goto out;
996 out:
997 free_DigestRepInner(&irep);
999 return ret;
1002 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1003 krb5_ntlm_req_set_flags(krb5_context context,
1004 krb5_ntlm ntlm,
1005 uint32_t flags)
1007 ntlm->request.flags = flags;
1008 return 0;
1011 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1012 krb5_ntlm_req_set_username(krb5_context context,
1013 krb5_ntlm ntlm,
1014 const char *username)
1016 ntlm->request.username = strdup(username);
1017 if (ntlm->request.username == NULL)
1018 return krb5_enomem(context);
1019 return 0;
1022 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1023 krb5_ntlm_req_set_targetname(krb5_context context,
1024 krb5_ntlm ntlm,
1025 const char *targetname)
1027 ntlm->request.targetname = strdup(targetname);
1028 if (ntlm->request.targetname == NULL)
1029 return krb5_enomem(context);
1030 return 0;
1033 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1034 krb5_ntlm_req_set_lm(krb5_context context,
1035 krb5_ntlm ntlm,
1036 void *hash, size_t len)
1038 ntlm->request.lm.data = malloc(len);
1039 if (ntlm->request.lm.data == NULL && len != 0)
1040 return krb5_enomem(context);
1041 ntlm->request.lm.length = len;
1042 memcpy(ntlm->request.lm.data, hash, len);
1043 return 0;
1046 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1047 krb5_ntlm_req_set_ntlm(krb5_context context,
1048 krb5_ntlm ntlm,
1049 void *hash, size_t len)
1051 ntlm->request.ntlm.data = malloc(len);
1052 if (ntlm->request.ntlm.data == NULL && len != 0)
1053 return krb5_enomem(context);
1054 ntlm->request.ntlm.length = len;
1055 memcpy(ntlm->request.ntlm.data, hash, len);
1056 return 0;
1059 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1060 krb5_ntlm_req_set_opaque(krb5_context context,
1061 krb5_ntlm ntlm,
1062 krb5_data *opaque)
1064 ntlm->request.opaque.data = malloc(opaque->length);
1065 if (ntlm->request.opaque.data == NULL && opaque->length != 0)
1066 return krb5_enomem(context);
1067 ntlm->request.opaque.length = opaque->length;
1068 memcpy(ntlm->request.opaque.data, opaque->data, opaque->length);
1069 return 0;
1072 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1073 krb5_ntlm_req_set_session(krb5_context context,
1074 krb5_ntlm ntlm,
1075 void *sessionkey, size_t length)
1077 ntlm->request.sessionkey = calloc(1, sizeof(*ntlm->request.sessionkey));
1078 if (ntlm->request.sessionkey == NULL)
1079 return krb5_enomem(context);
1080 ntlm->request.sessionkey->data = malloc(length);
1081 if (ntlm->request.sessionkey->data == NULL && length != 0)
1082 return krb5_enomem(context);
1083 memcpy(ntlm->request.sessionkey->data, sessionkey, length);
1084 ntlm->request.sessionkey->length = length;
1085 return 0;
1088 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1089 krb5_ntlm_rep_get_status(krb5_context context,
1090 krb5_ntlm ntlm)
1092 return ntlm->response.success ? TRUE : FALSE;
1095 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1096 krb5_ntlm_rep_get_sessionkey(krb5_context context,
1097 krb5_ntlm ntlm,
1098 krb5_data *data)
1100 if (ntlm->response.sessionkey == NULL) {
1101 krb5_set_error_message(context, EINVAL,
1102 N_("no ntlm session key", ""));
1103 return EINVAL;
1105 krb5_clear_error_message(context);
1106 return krb5_data_copy(data,
1107 ntlm->response.sessionkey->data,
1108 ntlm->response.sessionkey->length);
1112 * Get the supported/allowed mechanism for this principal.
1114 * @param context A Keberos context.
1115 * @param realm The realm of the KDC.
1116 * @param ccache The credential cache to use when talking to the KDC.
1117 * @param flags The supported mechanism.
1119 * @return Return an error code or 0.
1121 * @ingroup krb5_digest
1124 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1125 krb5_digest_probe(krb5_context context,
1126 krb5_realm realm,
1127 krb5_ccache ccache,
1128 unsigned *flags)
1130 DigestReqInner ireq;
1131 DigestRepInner irep;
1132 krb5_error_code ret;
1134 memset(&ireq, 0, sizeof(ireq));
1135 memset(&irep, 0, sizeof(irep));
1137 ireq.element = choice_DigestReqInner_supportedMechs;
1139 ret = digest_request(context, realm, ccache,
1140 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
1141 if (ret)
1142 goto out;
1144 if (irep.element == choice_DigestRepInner_error) {
1145 ret = irep.u.error.code;
1146 krb5_set_error_message(context, ret, "Digest probe error: %s",
1147 irep.u.error.reason);
1148 goto out;
1151 if (irep.element != choice_DigestRepInner_supportedMechs) {
1152 ret = EINVAL;
1153 krb5_set_error_message(context, ret, "Digest reply not an probe");
1154 goto out;
1157 *flags = DigestTypes2int(irep.u.supportedMechs);
1159 out:
1160 free_DigestRepInner(&irep);
1162 return ret;
1165 #endif /* HEIMDAL_SMALLER */