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