Home | History | Annotate | Line # | Download | only in krb5
      1 /*	$NetBSD: digest.c,v 1.2 2017/01/28 21:31:49 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2006 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  *
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * 3. Neither the name of the Institute nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include "krb5_locl.h"
     37 #include <krb5/digest_asn1.h>
     38 
     39 #ifndef HEIMDAL_SMALLER
     40 
     41 struct krb5_digest_data {
     42     char *cbtype;
     43     char *cbbinding;
     44 
     45     DigestInit init;
     46     DigestInitReply initReply;
     47     DigestRequest request;
     48     DigestResponse response;
     49 };
     50 
     51 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     52 krb5_digest_alloc(krb5_context context, krb5_digest *digest)
     53 {
     54     krb5_digest d;
     55 
     56     d = calloc(1, sizeof(*d));
     57     if (d == NULL) {
     58 	*digest = NULL;
     59 	return krb5_enomem(context);
     60     }
     61     *digest = d;
     62 
     63     return 0;
     64 }
     65 
     66 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
     67 krb5_digest_free(krb5_digest digest)
     68 {
     69     if (digest == NULL)
     70 	return;
     71     free_DigestInit(&digest->init);
     72     free_DigestInitReply(&digest->initReply);
     73     free_DigestRequest(&digest->request);
     74     free_DigestResponse(&digest->response);
     75     memset(digest, 0, sizeof(*digest));
     76     free(digest);
     77     return;
     78 }
     79 
     80 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     81 krb5_digest_set_server_cb(krb5_context context,
     82 			  krb5_digest digest,
     83 			  const char *type,
     84 			  const char *binding)
     85 {
     86     if (digest->init.channel) {
     87 	krb5_set_error_message(context, EINVAL,
     88 			       N_("server channel binding already set", ""));
     89 	return EINVAL;
     90     }
     91     digest->init.channel = calloc(1, sizeof(*digest->init.channel));
     92     if (digest->init.channel == NULL)
     93 	goto error;
     94 
     95     digest->init.channel->cb_type = strdup(type);
     96     if (digest->init.channel->cb_type == NULL)
     97 	goto error;
     98 
     99     digest->init.channel->cb_binding = strdup(binding);
    100     if (digest->init.channel->cb_binding == NULL)
    101 	goto error;
    102     return 0;
    103  error:
    104     if (digest->init.channel) {
    105 	free(digest->init.channel->cb_type);
    106 	free(digest->init.channel->cb_binding);
    107 	free(digest->init.channel);
    108 	digest->init.channel = NULL;
    109     }
    110     return krb5_enomem(context);
    111 }
    112 
    113 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    114 krb5_digest_set_type(krb5_context context,
    115 		     krb5_digest digest,
    116 		     const char *type)
    117 {
    118     if (digest->init.type) {
    119 	krb5_set_error_message(context, EINVAL, "client type already set");
    120 	return EINVAL;
    121     }
    122     digest->init.type = strdup(type);
    123     if (digest->init.type == NULL)
    124 	return krb5_enomem(context);
    125     return 0;
    126 }
    127 
    128 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    129 krb5_digest_set_hostname(krb5_context context,
    130 			 krb5_digest digest,
    131 			 const char *hostname)
    132 {
    133     if (digest->init.hostname) {
    134 	krb5_set_error_message(context, EINVAL, "server hostname already set");
    135 	return EINVAL;
    136     }
    137     digest->init.hostname = malloc(sizeof(*digest->init.hostname));
    138     if (digest->init.hostname == NULL)
    139 	return krb5_enomem(context);
    140     *digest->init.hostname = strdup(hostname);
    141     if (*digest->init.hostname == NULL) {
    142 	free(digest->init.hostname);
    143 	digest->init.hostname = NULL;
    144 	return krb5_enomem(context);
    145     }
    146     return 0;
    147 }
    148 
    149 KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
    150 krb5_digest_get_server_nonce(krb5_context context,
    151 			     krb5_digest digest)
    152 {
    153     return digest->initReply.nonce;
    154 }
    155 
    156 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    157 krb5_digest_set_server_nonce(krb5_context context,
    158 			     krb5_digest digest,
    159 			     const char *nonce)
    160 {
    161     if (digest->request.serverNonce) {
    162 	krb5_set_error_message(context, EINVAL, N_("nonce already set", ""));
    163 	return EINVAL;
    164     }
    165     digest->request.serverNonce = strdup(nonce);
    166     if (digest->request.serverNonce == NULL)
    167 	return krb5_enomem(context);
    168     return 0;
    169 }
    170 
    171 KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
    172 krb5_digest_get_opaque(krb5_context context,
    173 		       krb5_digest digest)
    174 {
    175     return digest->initReply.opaque;
    176 }
    177 
    178 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    179 krb5_digest_set_opaque(krb5_context context,
    180 		       krb5_digest digest,
    181 		       const char *opaque)
    182 {
    183     if (digest->request.opaque) {
    184 	krb5_set_error_message(context, EINVAL, "opaque already set");
    185 	return EINVAL;
    186     }
    187     digest->request.opaque = strdup(opaque);
    188     if (digest->request.opaque == NULL)
    189 	return krb5_enomem(context);
    190     return 0;
    191 }
    192 
    193 KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
    194 krb5_digest_get_identifier(krb5_context context,
    195 			   krb5_digest digest)
    196 {
    197     if (digest->initReply.identifier == NULL)
    198 	return NULL;
    199     return *digest->initReply.identifier;
    200 }
    201 
    202 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    203 krb5_digest_set_identifier(krb5_context context,
    204 			   krb5_digest digest,
    205 			   const char *id)
    206 {
    207     if (digest->request.identifier) {
    208 	krb5_set_error_message(context, EINVAL, N_("identifier already set", ""));
    209 	return EINVAL;
    210     }
    211     digest->request.identifier = calloc(1, sizeof(*digest->request.identifier));
    212     if (digest->request.identifier == NULL)
    213 	return krb5_enomem(context);
    214     *digest->request.identifier = strdup(id);
    215     if (*digest->request.identifier == NULL) {
    216 	free(digest->request.identifier);
    217 	digest->request.identifier = NULL;
    218 	return krb5_enomem(context);
    219     }
    220     return 0;
    221 }
    222 
    223 static krb5_error_code
    224 digest_request(krb5_context context,
    225 	       krb5_realm realm,
    226 	       krb5_ccache ccache,
    227 	       krb5_key_usage usage,
    228 	       const DigestReqInner *ireq,
    229 	       DigestRepInner *irep)
    230 {
    231     DigestREQ req;
    232     DigestREP rep;
    233     krb5_error_code ret;
    234     krb5_data data, data2;
    235     size_t size = 0;
    236     krb5_crypto crypto = NULL;
    237     krb5_auth_context ac = NULL;
    238     krb5_principal principal = NULL;
    239     krb5_ccache id = NULL;
    240     krb5_realm r = NULL;
    241 
    242     krb5_data_zero(&data);
    243     krb5_data_zero(&data2);
    244     memset(&req, 0, sizeof(req));
    245     memset(&rep, 0, sizeof(rep));
    246 
    247     if (ccache == NULL) {
    248 	ret = krb5_cc_default(context, &id);
    249 	if (ret)
    250 	    goto out;
    251     } else
    252 	id = ccache;
    253 
    254     if (realm == NULL) {
    255 	ret = krb5_get_default_realm(context, &r);
    256 	if (ret)
    257 	    goto out;
    258     } else
    259 	r = realm;
    260 
    261     /*
    262      *
    263      */
    264 
    265     ret = krb5_make_principal(context, &principal,
    266 			      r, KRB5_DIGEST_NAME, r, NULL);
    267     if (ret)
    268 	goto out;
    269 
    270     ASN1_MALLOC_ENCODE(DigestReqInner, data.data, data.length,
    271 		       ireq, &size, ret);
    272     if (ret) {
    273 	krb5_set_error_message(context, ret,
    274 			       N_("Failed to encode digest inner request", ""));
    275 	goto out;
    276     }
    277     if (size != data.length)
    278 	krb5_abortx(context, "ASN.1 internal encoder error");
    279 
    280     ret = krb5_mk_req_exact(context, &ac,
    281 			    AP_OPTS_USE_SUBKEY|AP_OPTS_MUTUAL_REQUIRED,
    282 			    principal, NULL, id, &req.apReq);
    283     if (ret)
    284 	goto out;
    285 
    286     {
    287 	krb5_keyblock *key;
    288 
    289 	ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
    290 	if (ret)
    291 	    goto out;
    292 	if (key == NULL) {
    293 	    ret = EINVAL;
    294 	    krb5_set_error_message(context, ret,
    295 				   N_("Digest failed to get local subkey", ""));
    296 	    goto out;
    297 	}
    298 
    299 	ret = krb5_crypto_init(context, key, 0, &crypto);
    300 	krb5_free_keyblock (context, key);
    301 	if (ret)
    302 	    goto out;
    303     }
    304 
    305     ret = krb5_encrypt_EncryptedData(context, crypto, usage,
    306 				     data.data, data.length, 0,
    307 				     &req.innerReq);
    308     if (ret)
    309 	goto out;
    310 
    311     krb5_data_free(&data);
    312 
    313     ASN1_MALLOC_ENCODE(DigestREQ, data.data, data.length,
    314 		       &req, &size, ret);
    315     if (ret) {
    316 	krb5_set_error_message(context, ret,
    317 			       N_("Failed to encode DigestREQest", ""));
    318 	goto out;
    319     }
    320     if (size != data.length)
    321 	krb5_abortx(context, "ASN.1 internal encoder error");
    322 
    323     ret = krb5_sendto_kdc(context, &data, &r, &data2);
    324     if (ret)
    325 	goto out;
    326 
    327     ret = decode_DigestREP(data2.data, data2.length, &rep, NULL);
    328     if (ret) {
    329 	krb5_set_error_message(context, ret,
    330 			       N_("Failed to parse digest response", ""));
    331 	goto out;
    332     }
    333 
    334     {
    335 	krb5_ap_rep_enc_part *repl;
    336 
    337 	ret = krb5_rd_rep(context, ac, &rep.apRep, &repl);
    338 	if (ret)
    339 	    goto out;
    340 
    341 	krb5_free_ap_rep_enc_part(context, repl);
    342     }
    343     {
    344 	krb5_keyblock *key;
    345 
    346 	ret = krb5_auth_con_getremotesubkey(context, ac, &key);
    347 	if (ret)
    348 	    goto out;
    349 	if (key == NULL) {
    350 	    ret = EINVAL;
    351 	    krb5_set_error_message(context, ret,
    352 				   N_("Digest reply have no remote subkey", ""));
    353 	    goto out;
    354 	}
    355 
    356 	krb5_crypto_destroy(context, crypto);
    357 	ret = krb5_crypto_init(context, key, 0, &crypto);
    358 	krb5_free_keyblock (context, key);
    359 	if (ret)
    360 	    goto out;
    361     }
    362 
    363     krb5_data_free(&data);
    364     ret = krb5_decrypt_EncryptedData(context, crypto, usage,
    365 				     &rep.innerRep, &data);
    366     if (ret)
    367 	goto out;
    368 
    369     ret = decode_DigestRepInner(data.data, data.length, irep, NULL);
    370     if (ret) {
    371 	krb5_set_error_message(context, ret,
    372 			       N_("Failed to decode digest inner reply", ""));
    373 	goto out;
    374     }
    375 
    376  out:
    377     if (ccache == NULL && id)
    378 	krb5_cc_close(context, id);
    379     if (realm == NULL && r)
    380 	free(r);
    381     if (crypto)
    382 	krb5_crypto_destroy(context, crypto);
    383     if (ac)
    384 	krb5_auth_con_free(context, ac);
    385     if (principal)
    386 	krb5_free_principal(context, principal);
    387 
    388     krb5_data_free(&data);
    389     krb5_data_free(&data2);
    390 
    391     free_DigestREQ(&req);
    392     free_DigestREP(&rep);
    393 
    394     return ret;
    395 }
    396 
    397 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    398 krb5_digest_init_request(krb5_context context,
    399 			 krb5_digest digest,
    400 			 krb5_realm realm,
    401 			 krb5_ccache ccache)
    402 {
    403     DigestReqInner ireq;
    404     DigestRepInner irep;
    405     krb5_error_code ret;
    406 
    407     memset(&ireq, 0, sizeof(ireq));
    408     memset(&irep, 0, sizeof(irep));
    409 
    410     if (digest->init.type == NULL) {
    411 	krb5_set_error_message(context, EINVAL,
    412 			       N_("Type missing from init req", ""));
    413 	return EINVAL;
    414     }
    415 
    416     ireq.element = choice_DigestReqInner_init;
    417     ireq.u.init = digest->init;
    418 
    419     ret = digest_request(context, realm, ccache,
    420 			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
    421     if (ret)
    422 	goto out;
    423 
    424     if (irep.element == choice_DigestRepInner_error) {
    425 	ret = irep.u.error.code;
    426 	krb5_set_error_message(context, ret, N_("Digest init error: %s", ""),
    427 			       irep.u.error.reason);
    428 	goto out;
    429     }
    430 
    431     if (irep.element != choice_DigestRepInner_initReply) {
    432 	ret = EINVAL;
    433 	krb5_set_error_message(context, ret,
    434 			       N_("digest reply not an initReply", ""));
    435 	goto out;
    436     }
    437 
    438     ret = copy_DigestInitReply(&irep.u.initReply, &digest->initReply);
    439     if (ret) {
    440 	krb5_set_error_message(context, ret,
    441 			       N_("Failed to copy initReply", ""));
    442 	goto out;
    443     }
    444 
    445  out:
    446     free_DigestRepInner(&irep);
    447 
    448     return ret;
    449 }
    450 
    451 
    452 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    453 krb5_digest_set_client_nonce(krb5_context context,
    454 			     krb5_digest digest,
    455 			     const char *nonce)
    456 {
    457     if (digest->request.clientNonce) {
    458 	krb5_set_error_message(context, EINVAL,
    459 			       N_("clientNonce already set", ""));
    460 	return EINVAL;
    461     }
    462     digest->request.clientNonce =
    463 	calloc(1, sizeof(*digest->request.clientNonce));
    464     if (digest->request.clientNonce == NULL)
    465 	return krb5_enomem(context);
    466     *digest->request.clientNonce = strdup(nonce);
    467     if (*digest->request.clientNonce == NULL) {
    468 	free(digest->request.clientNonce);
    469 	digest->request.clientNonce = NULL;
    470 	return krb5_enomem(context);
    471     }
    472     return 0;
    473 }
    474 
    475 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    476 krb5_digest_set_digest(krb5_context context,
    477 		       krb5_digest digest,
    478 		       const char *dgst)
    479 {
    480     if (digest->request.digest) {
    481 	krb5_set_error_message(context, EINVAL,
    482 			       N_("digest already set", ""));
    483 	return EINVAL;
    484     }
    485     digest->request.digest = strdup(dgst);
    486     if (digest->request.digest == NULL)
    487 	return krb5_enomem(context);
    488     return 0;
    489 }
    490 
    491 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    492 krb5_digest_set_username(krb5_context context,
    493 			 krb5_digest digest,
    494 			 const char *username)
    495 {
    496     if (digest->request.username) {
    497 	krb5_set_error_message(context, EINVAL, "username already set");
    498 	return EINVAL;
    499     }
    500     digest->request.username = strdup(username);
    501     if (digest->request.username == NULL)
    502 	return krb5_enomem(context);
    503     return 0;
    504 }
    505 
    506 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    507 krb5_digest_set_authid(krb5_context context,
    508 		       krb5_digest digest,
    509 		       const char *authid)
    510 {
    511     if (digest->request.authid) {
    512 	krb5_set_error_message(context, EINVAL, "authid already set");
    513 	return EINVAL;
    514     }
    515     digest->request.authid = malloc(sizeof(*digest->request.authid));
    516     if (digest->request.authid == NULL)
    517 	return krb5_enomem(context);
    518     *digest->request.authid = strdup(authid);
    519     if (*digest->request.authid == NULL) {
    520 	free(digest->request.authid);
    521 	digest->request.authid = NULL;
    522 	return krb5_enomem(context);
    523     }
    524     return 0;
    525 }
    526 
    527 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    528 krb5_digest_set_authentication_user(krb5_context context,
    529 				    krb5_digest digest,
    530 				    krb5_principal authentication_user)
    531 {
    532     krb5_error_code ret;
    533 
    534     if (digest->request.authentication_user) {
    535 	krb5_set_error_message(context, EINVAL,
    536 			       N_("authentication_user already set", ""));
    537 	return EINVAL;
    538     }
    539     ret = krb5_copy_principal(context,
    540 			      authentication_user,
    541 			      &digest->request.authentication_user);
    542     if (ret)
    543 	return ret;
    544     return 0;
    545 }
    546 
    547 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    548 krb5_digest_set_realm(krb5_context context,
    549 		      krb5_digest digest,
    550 		      const char *realm)
    551 {
    552     if (digest->request.realm) {
    553 	krb5_set_error_message(context, EINVAL, "realm already set");
    554 	return EINVAL;
    555     }
    556     digest->request.realm = malloc(sizeof(*digest->request.realm));
    557     if (digest->request.realm == NULL)
    558 	return krb5_enomem(context);
    559     *digest->request.realm = strdup(realm);
    560     if (*digest->request.realm == NULL) {
    561 	free(digest->request.realm);
    562 	digest->request.realm = NULL;
    563 	return krb5_enomem(context);
    564     }
    565     return 0;
    566 }
    567 
    568 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    569 krb5_digest_set_method(krb5_context context,
    570 		       krb5_digest digest,
    571 		       const char *method)
    572 {
    573     if (digest->request.method) {
    574 	krb5_set_error_message(context, EINVAL,
    575 			       N_("method already set", ""));
    576 	return EINVAL;
    577     }
    578     digest->request.method = malloc(sizeof(*digest->request.method));
    579     if (digest->request.method == NULL)
    580 	return krb5_enomem(context);
    581     *digest->request.method = strdup(method);
    582     if (*digest->request.method == NULL) {
    583 	free(digest->request.method);
    584 	digest->request.method = NULL;
    585 	return krb5_enomem(context);
    586     }
    587     return 0;
    588 }
    589 
    590 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    591 krb5_digest_set_uri(krb5_context context,
    592 		    krb5_digest digest,
    593 		    const char *uri)
    594 {
    595     if (digest->request.uri) {
    596 	krb5_set_error_message(context, EINVAL, N_("uri already set", ""));
    597 	return EINVAL;
    598     }
    599     digest->request.uri = malloc(sizeof(*digest->request.uri));
    600     if (digest->request.uri == NULL)
    601 	return krb5_enomem(context);
    602     *digest->request.uri = strdup(uri);
    603     if (*digest->request.uri == NULL) {
    604 	free(digest->request.uri);
    605 	digest->request.uri = NULL;
    606 	return krb5_enomem(context);
    607     }
    608     return 0;
    609 }
    610 
    611 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    612 krb5_digest_set_nonceCount(krb5_context context,
    613 			   krb5_digest digest,
    614 			   const char *nonce_count)
    615 {
    616     if (digest->request.nonceCount) {
    617 	krb5_set_error_message(context, EINVAL,
    618 			       N_("nonceCount already set", ""));
    619 	return EINVAL;
    620     }
    621     digest->request.nonceCount =
    622 	malloc(sizeof(*digest->request.nonceCount));
    623     if (digest->request.nonceCount == NULL)
    624 	return krb5_enomem(context);
    625     *digest->request.nonceCount = strdup(nonce_count);
    626     if (*digest->request.nonceCount == NULL) {
    627 	free(digest->request.nonceCount);
    628 	digest->request.nonceCount = NULL;
    629 	return krb5_enomem(context);
    630     }
    631     return 0;
    632 }
    633 
    634 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    635 krb5_digest_set_qop(krb5_context context,
    636 		    krb5_digest digest,
    637 		    const char *qop)
    638 {
    639     if (digest->request.qop) {
    640 	krb5_set_error_message(context, EINVAL, "qop already set");
    641 	return EINVAL;
    642     }
    643     digest->request.qop = malloc(sizeof(*digest->request.qop));
    644     if (digest->request.qop == NULL)
    645 	return krb5_enomem(context);
    646     *digest->request.qop = strdup(qop);
    647     if (*digest->request.qop == NULL) {
    648 	free(digest->request.qop);
    649 	digest->request.qop = NULL;
    650 	return krb5_enomem(context);
    651     }
    652     return 0;
    653 }
    654 
    655 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
    656 krb5_digest_set_responseData(krb5_context context,
    657 			     krb5_digest digest,
    658 			     const char *response)
    659 {
    660     digest->request.responseData = strdup(response);
    661     if (digest->request.responseData == NULL)
    662 	return krb5_enomem(context);
    663     return 0;
    664 }
    665 
    666 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    667 krb5_digest_request(krb5_context context,
    668 		    krb5_digest digest,
    669 		    krb5_realm realm,
    670 		    krb5_ccache ccache)
    671 {
    672     DigestReqInner ireq;
    673     DigestRepInner irep;
    674     krb5_error_code ret;
    675 
    676     memset(&ireq, 0, sizeof(ireq));
    677     memset(&irep, 0, sizeof(irep));
    678 
    679     ireq.element = choice_DigestReqInner_digestRequest;
    680     ireq.u.digestRequest = digest->request;
    681 
    682     if (digest->request.type == NULL) {
    683 	if (digest->init.type == NULL) {
    684 	    krb5_set_error_message(context, EINVAL,
    685 				   N_("Type missing from req", ""));
    686 	    return EINVAL;
    687 	}
    688 	ireq.u.digestRequest.type = digest->init.type;
    689     }
    690 
    691     if (ireq.u.digestRequest.digest == NULL) {
    692 	static char md5[] = "md5";
    693 	ireq.u.digestRequest.digest = md5;
    694     }
    695 
    696     ret = digest_request(context, realm, ccache,
    697 			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
    698     if (ret)
    699 	return ret;
    700 
    701     if (irep.element == choice_DigestRepInner_error) {
    702 	ret = irep.u.error.code;
    703 	krb5_set_error_message(context, ret,
    704 			       N_("Digest response error: %s", ""),
    705 			       irep.u.error.reason);
    706 	goto out;
    707     }
    708 
    709     if (irep.element != choice_DigestRepInner_response) {
    710 	krb5_set_error_message(context, EINVAL,
    711 			       N_("digest reply not an DigestResponse", ""));
    712 	ret = EINVAL;
    713 	goto out;
    714     }
    715 
    716     ret = copy_DigestResponse(&irep.u.response, &digest->response);
    717     if (ret) {
    718 	krb5_set_error_message(context, ret,
    719 			       N_("Failed to copy initReply,", ""));
    720 	goto out;
    721     }
    722 
    723  out:
    724     free_DigestRepInner(&irep);
    725 
    726     return ret;
    727 }
    728 
    729 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
    730 krb5_digest_rep_get_status(krb5_context context,
    731 			   krb5_digest digest)
    732 {
    733     return digest->response.success ? TRUE : FALSE;
    734 }
    735 
    736 KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
    737 krb5_digest_get_rsp(krb5_context context,
    738 		    krb5_digest digest)
    739 {
    740     if (digest->response.rsp == NULL)
    741 	return NULL;
    742     return *digest->response.rsp;
    743 }
    744 
    745 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    746 krb5_digest_get_tickets(krb5_context context,
    747 			krb5_digest digest,
    748 			Ticket **tickets)
    749 {
    750     *tickets = NULL;
    751     return 0;
    752 }
    753 
    754 
    755 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    756 krb5_digest_get_client_binding(krb5_context context,
    757 			       krb5_digest digest,
    758 			       char **type,
    759 			       char **binding)
    760 {
    761     if (digest->response.channel) {
    762 	*type = strdup(digest->response.channel->cb_type);
    763 	*binding = strdup(digest->response.channel->cb_binding);
    764 	if (*type == NULL || *binding == NULL) {
    765 	    free(*type);
    766 	    free(*binding);
    767 	    return krb5_enomem(context);
    768 	}
    769     } else {
    770 	*type = NULL;
    771 	*binding = NULL;
    772     }
    773     return 0;
    774 }
    775 
    776 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    777 krb5_digest_get_session_key(krb5_context context,
    778 			    krb5_digest digest,
    779 			    krb5_data *data)
    780 {
    781     krb5_error_code ret;
    782 
    783     krb5_data_zero(data);
    784     if (digest->response.session_key == NULL)
    785 	return 0;
    786     ret = der_copy_octet_string(digest->response.session_key, data);
    787     if (ret)
    788 	krb5_clear_error_message(context);
    789 
    790     return ret;
    791 }
    792 
    793 struct krb5_ntlm_data {
    794     NTLMInit init;
    795     NTLMInitReply initReply;
    796     NTLMRequest request;
    797     NTLMResponse response;
    798 };
    799 
    800 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    801 krb5_ntlm_alloc(krb5_context context,
    802 		krb5_ntlm *ntlm)
    803 {
    804     *ntlm = calloc(1, sizeof(**ntlm));
    805     if (*ntlm == NULL)
    806 	return krb5_enomem(context);
    807     return 0;
    808 }
    809 
    810 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    811 krb5_ntlm_free(krb5_context context, krb5_ntlm ntlm)
    812 {
    813     free_NTLMInit(&ntlm->init);
    814     free_NTLMInitReply(&ntlm->initReply);
    815     free_NTLMRequest(&ntlm->request);
    816     free_NTLMResponse(&ntlm->response);
    817     memset(ntlm, 0, sizeof(*ntlm));
    818     free(ntlm);
    819     return 0;
    820 }
    821 
    822 
    823 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    824 krb5_ntlm_init_request(krb5_context context,
    825 		       krb5_ntlm ntlm,
    826 		       krb5_realm realm,
    827 		       krb5_ccache ccache,
    828 		       uint32_t flags,
    829 		       const char *hostname,
    830 		       const char *domainname)
    831 {
    832     DigestReqInner ireq;
    833     DigestRepInner irep;
    834     krb5_error_code ret;
    835 
    836     memset(&ireq, 0, sizeof(ireq));
    837     memset(&irep, 0, sizeof(irep));
    838 
    839     ntlm->init.flags = flags;
    840     if (hostname) {
    841 	ALLOC(ntlm->init.hostname, 1);
    842 	*ntlm->init.hostname = strdup(hostname);
    843     }
    844     if (domainname) {
    845 	ALLOC(ntlm->init.domain, 1);
    846 	*ntlm->init.domain = strdup(domainname);
    847     }
    848 
    849     ireq.element = choice_DigestReqInner_ntlmInit;
    850     ireq.u.ntlmInit = ntlm->init;
    851 
    852     ret = digest_request(context, realm, ccache,
    853 			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
    854     if (ret)
    855 	goto out;
    856 
    857     if (irep.element == choice_DigestRepInner_error) {
    858 	ret = irep.u.error.code;
    859 	krb5_set_error_message(context, ret, N_("Digest init error: %s", ""),
    860 			       irep.u.error.reason);
    861 	goto out;
    862     }
    863 
    864     if (irep.element != choice_DigestRepInner_ntlmInitReply) {
    865 	ret = EINVAL;
    866 	krb5_set_error_message(context, ret,
    867 			       N_("ntlm reply not an initReply", ""));
    868 	goto out;
    869     }
    870 
    871     ret = copy_NTLMInitReply(&irep.u.ntlmInitReply, &ntlm->initReply);
    872     if (ret) {
    873 	krb5_set_error_message(context, ret,
    874 			       N_("Failed to copy initReply", ""));
    875 	goto out;
    876     }
    877 
    878  out:
    879     free_DigestRepInner(&irep);
    880 
    881     return ret;
    882 }
    883 
    884 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    885 krb5_ntlm_init_get_flags(krb5_context context,
    886 			 krb5_ntlm ntlm,
    887 			 uint32_t *flags)
    888 {
    889     *flags = ntlm->initReply.flags;
    890     return 0;
    891 }
    892 
    893 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    894 krb5_ntlm_init_get_challenge(krb5_context context,
    895 			     krb5_ntlm ntlm,
    896 			     krb5_data *challenge)
    897 {
    898     krb5_error_code ret;
    899 
    900     ret = der_copy_octet_string(&ntlm->initReply.challenge, challenge);
    901     if (ret)
    902 	krb5_clear_error_message(context);
    903 
    904     return ret;
    905 }
    906 
    907 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    908 krb5_ntlm_init_get_opaque(krb5_context context,
    909 			  krb5_ntlm ntlm,
    910 			  krb5_data *opaque)
    911 {
    912     krb5_error_code ret;
    913 
    914     ret = der_copy_octet_string(&ntlm->initReply.opaque, opaque);
    915     if (ret)
    916 	krb5_clear_error_message(context);
    917 
    918     return ret;
    919 }
    920 
    921 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    922 krb5_ntlm_init_get_targetname(krb5_context context,
    923 			      krb5_ntlm ntlm,
    924 			      char **name)
    925 {
    926     *name = strdup(ntlm->initReply.targetname);
    927     if (*name == NULL)
    928 	return krb5_enomem(context);
    929     return 0;
    930 }
    931 
    932 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    933 krb5_ntlm_init_get_targetinfo(krb5_context context,
    934 			      krb5_ntlm ntlm,
    935 			      krb5_data *data)
    936 {
    937     krb5_error_code ret;
    938 
    939     if (ntlm->initReply.targetinfo == NULL) {
    940 	krb5_data_zero(data);
    941 	return 0;
    942     }
    943 
    944     ret = krb5_data_copy(data,
    945 			 ntlm->initReply.targetinfo->data,
    946 			 ntlm->initReply.targetinfo->length);
    947     if (ret) {
    948 	krb5_clear_error_message(context);
    949 	return ret;
    950     }
    951     return 0;
    952 }
    953 
    954 
    955 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    956 krb5_ntlm_request(krb5_context context,
    957 		  krb5_ntlm ntlm,
    958 		  krb5_realm realm,
    959 		  krb5_ccache ccache)
    960 {
    961     DigestReqInner ireq;
    962     DigestRepInner irep;
    963     krb5_error_code ret;
    964 
    965     memset(&ireq, 0, sizeof(ireq));
    966     memset(&irep, 0, sizeof(irep));
    967 
    968     ireq.element = choice_DigestReqInner_ntlmRequest;
    969     ireq.u.ntlmRequest = ntlm->request;
    970 
    971     ret = digest_request(context, realm, ccache,
    972 			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
    973     if (ret)
    974 	return ret;
    975 
    976     if (irep.element == choice_DigestRepInner_error) {
    977 	ret = irep.u.error.code;
    978 	krb5_set_error_message(context, ret,
    979 			       N_("NTLM response error: %s", ""),
    980 			       irep.u.error.reason);
    981 	goto out;
    982     }
    983 
    984     if (irep.element != choice_DigestRepInner_ntlmResponse) {
    985 	ret = EINVAL;
    986 	krb5_set_error_message(context, ret,
    987 			       N_("NTLM reply not an NTLMResponse", ""));
    988 	goto out;
    989     }
    990 
    991     ret = copy_NTLMResponse(&irep.u.ntlmResponse, &ntlm->response);
    992     if (ret) {
    993 	krb5_set_error_message(context, ret,
    994 			       N_("Failed to copy NTLMResponse", ""));
    995 	goto out;
    996     }
    997 
    998  out:
    999     free_DigestRepInner(&irep);
   1000 
   1001     return ret;
   1002 }
   1003 
   1004 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1005 krb5_ntlm_req_set_flags(krb5_context context,
   1006 			krb5_ntlm ntlm,
   1007 			uint32_t flags)
   1008 {
   1009     ntlm->request.flags = flags;
   1010     return 0;
   1011 }
   1012 
   1013 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1014 krb5_ntlm_req_set_username(krb5_context context,
   1015 			   krb5_ntlm ntlm,
   1016 			   const char *username)
   1017 {
   1018     ntlm->request.username = strdup(username);
   1019     if (ntlm->request.username == NULL)
   1020 	return krb5_enomem(context);
   1021     return 0;
   1022 }
   1023 
   1024 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1025 krb5_ntlm_req_set_targetname(krb5_context context,
   1026 			     krb5_ntlm ntlm,
   1027 			     const char *targetname)
   1028 {
   1029     ntlm->request.targetname = strdup(targetname);
   1030     if (ntlm->request.targetname == NULL)
   1031 	return krb5_enomem(context);
   1032     return 0;
   1033 }
   1034 
   1035 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1036 krb5_ntlm_req_set_lm(krb5_context context,
   1037 		     krb5_ntlm ntlm,
   1038 		     void *hash, size_t len)
   1039 {
   1040     ntlm->request.lm.data = malloc(len);
   1041     if (ntlm->request.lm.data == NULL && len != 0)
   1042 	return krb5_enomem(context);
   1043     ntlm->request.lm.length = len;
   1044     memcpy(ntlm->request.lm.data, hash, len);
   1045     return 0;
   1046 }
   1047 
   1048 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1049 krb5_ntlm_req_set_ntlm(krb5_context context,
   1050 		       krb5_ntlm ntlm,
   1051 		       void *hash, size_t len)
   1052 {
   1053     ntlm->request.ntlm.data = malloc(len);
   1054     if (ntlm->request.ntlm.data == NULL && len != 0)
   1055 	return krb5_enomem(context);
   1056     ntlm->request.ntlm.length = len;
   1057     memcpy(ntlm->request.ntlm.data, hash, len);
   1058     return 0;
   1059 }
   1060 
   1061 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1062 krb5_ntlm_req_set_opaque(krb5_context context,
   1063 			 krb5_ntlm ntlm,
   1064 			 krb5_data *opaque)
   1065 {
   1066     ntlm->request.opaque.data = malloc(opaque->length);
   1067     if (ntlm->request.opaque.data == NULL && opaque->length != 0)
   1068 	return krb5_enomem(context);
   1069     ntlm->request.opaque.length = opaque->length;
   1070     memcpy(ntlm->request.opaque.data, opaque->data, opaque->length);
   1071     return 0;
   1072 }
   1073 
   1074 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1075 krb5_ntlm_req_set_session(krb5_context context,
   1076 			  krb5_ntlm ntlm,
   1077 			  void *sessionkey, size_t length)
   1078 {
   1079     ntlm->request.sessionkey = calloc(1, sizeof(*ntlm->request.sessionkey));
   1080     if (ntlm->request.sessionkey == NULL)
   1081 	return krb5_enomem(context);
   1082     ntlm->request.sessionkey->data = malloc(length);
   1083     if (ntlm->request.sessionkey->data == NULL && length != 0)
   1084 	return krb5_enomem(context);
   1085     memcpy(ntlm->request.sessionkey->data, sessionkey, length);
   1086     ntlm->request.sessionkey->length = length;
   1087     return 0;
   1088 }
   1089 
   1090 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
   1091 krb5_ntlm_rep_get_status(krb5_context context,
   1092 			 krb5_ntlm ntlm)
   1093 {
   1094     return ntlm->response.success ? TRUE : FALSE;
   1095 }
   1096 
   1097 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1098 krb5_ntlm_rep_get_sessionkey(krb5_context context,
   1099 			     krb5_ntlm ntlm,
   1100 			     krb5_data *data)
   1101 {
   1102     if (ntlm->response.sessionkey == NULL) {
   1103 	krb5_set_error_message(context, EINVAL,
   1104 			       N_("no ntlm session key", ""));
   1105 	return EINVAL;
   1106     }
   1107     krb5_clear_error_message(context);
   1108     return krb5_data_copy(data,
   1109 			  ntlm->response.sessionkey->data,
   1110 			  ntlm->response.sessionkey->length);
   1111 }
   1112 
   1113 /**
   1114  * Get the supported/allowed mechanism for this principal.
   1115  *
   1116  * @param context A Keberos context.
   1117  * @param realm The realm of the KDC.
   1118  * @param ccache The credential cache to use when talking to the KDC.
   1119  * @param flags The supported mechanism.
   1120  *
   1121  * @return Return an error code or 0.
   1122  *
   1123  * @ingroup krb5_digest
   1124  */
   1125 
   1126 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1127 krb5_digest_probe(krb5_context context,
   1128 		  krb5_realm realm,
   1129 		  krb5_ccache ccache,
   1130 		  unsigned *flags)
   1131 {
   1132     DigestReqInner ireq;
   1133     DigestRepInner irep;
   1134     krb5_error_code ret;
   1135 
   1136     memset(&ireq, 0, sizeof(ireq));
   1137     memset(&irep, 0, sizeof(irep));
   1138 
   1139     ireq.element = choice_DigestReqInner_supportedMechs;
   1140 
   1141     ret = digest_request(context, realm, ccache,
   1142 			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
   1143     if (ret)
   1144 	goto out;
   1145 
   1146     if (irep.element == choice_DigestRepInner_error) {
   1147 	ret = irep.u.error.code;
   1148 	krb5_set_error_message(context, ret, "Digest probe error: %s",
   1149 			       irep.u.error.reason);
   1150 	goto out;
   1151     }
   1152 
   1153     if (irep.element != choice_DigestRepInner_supportedMechs) {
   1154 	ret = EINVAL;
   1155 	krb5_set_error_message(context, ret, "Digest reply not an probe");
   1156 	goto out;
   1157     }
   1158 
   1159     *flags = DigestTypes2int(irep.u.supportedMechs);
   1160 
   1161  out:
   1162     free_DigestRepInner(&irep);
   1163 
   1164     return ret;
   1165 }
   1166 
   1167 #endif /* HEIMDAL_SMALLER */
   1168