1 /*	$NetBSD: digest.c,v 1.2 2017/01/28 21:31:49 christos 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 	return krb5_enomem(context);
60     }
61     *digest = d;
62 
63     return 0;
64 }
65 
66 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_digest_free(krb5_digest digest)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
krb5_digest_set_server_cb(krb5_context context,krb5_digest digest,const char * type,const char * binding)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
krb5_digest_set_type(krb5_context context,krb5_digest digest,const char * type)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
krb5_digest_set_hostname(krb5_context context,krb5_digest digest,const char * hostname)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
krb5_digest_get_server_nonce(krb5_context context,krb5_digest digest)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
krb5_digest_set_server_nonce(krb5_context context,krb5_digest digest,const char * nonce)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
krb5_digest_get_opaque(krb5_context context,krb5_digest digest)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
krb5_digest_set_opaque(krb5_context context,krb5_digest digest,const char * opaque)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
krb5_digest_get_identifier(krb5_context context,krb5_digest digest)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
krb5_digest_set_identifier(krb5_context context,krb5_digest digest,const char * id)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
digest_request(krb5_context context,krb5_realm realm,krb5_ccache ccache,krb5_key_usage usage,const DigestReqInner * ireq,DigestRepInner * irep)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
krb5_digest_init_request(krb5_context context,krb5_digest digest,krb5_realm realm,krb5_ccache ccache)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
krb5_digest_set_client_nonce(krb5_context context,krb5_digest digest,const char * nonce)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
krb5_digest_set_digest(krb5_context context,krb5_digest digest,const char * dgst)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
krb5_digest_set_username(krb5_context context,krb5_digest digest,const char * username)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
krb5_digest_set_authid(krb5_context context,krb5_digest digest,const char * authid)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
krb5_digest_set_authentication_user(krb5_context context,krb5_digest digest,krb5_principal authentication_user)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
krb5_digest_set_realm(krb5_context context,krb5_digest digest,const char * realm)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
krb5_digest_set_method(krb5_context context,krb5_digest digest,const char * method)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
krb5_digest_set_uri(krb5_context context,krb5_digest digest,const char * uri)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
krb5_digest_set_nonceCount(krb5_context context,krb5_digest digest,const char * nonce_count)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
krb5_digest_set_qop(krb5_context context,krb5_digest digest,const char * qop)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
krb5_digest_set_responseData(krb5_context context,krb5_digest digest,const char * response)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
krb5_digest_request(krb5_context context,krb5_digest digest,krb5_realm realm,krb5_ccache ccache)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
krb5_digest_rep_get_status(krb5_context context,krb5_digest digest)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
krb5_digest_get_rsp(krb5_context context,krb5_digest digest)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
krb5_digest_get_tickets(krb5_context context,krb5_digest digest,Ticket ** tickets)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
krb5_digest_get_client_binding(krb5_context context,krb5_digest digest,char ** type,char ** binding)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
krb5_digest_get_session_key(krb5_context context,krb5_digest digest,krb5_data * data)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
krb5_ntlm_alloc(krb5_context context,krb5_ntlm * ntlm)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
krb5_ntlm_free(krb5_context context,krb5_ntlm ntlm)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
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)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
krb5_ntlm_init_get_flags(krb5_context context,krb5_ntlm ntlm,uint32_t * flags)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
krb5_ntlm_init_get_challenge(krb5_context context,krb5_ntlm ntlm,krb5_data * challenge)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
krb5_ntlm_init_get_opaque(krb5_context context,krb5_ntlm ntlm,krb5_data * opaque)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
krb5_ntlm_init_get_targetname(krb5_context context,krb5_ntlm ntlm,char ** name)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
krb5_ntlm_init_get_targetinfo(krb5_context context,krb5_ntlm ntlm,krb5_data * data)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
krb5_ntlm_request(krb5_context context,krb5_ntlm ntlm,krb5_realm realm,krb5_ccache ccache)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
krb5_ntlm_req_set_flags(krb5_context context,krb5_ntlm ntlm,uint32_t flags)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
krb5_ntlm_req_set_username(krb5_context context,krb5_ntlm ntlm,const char * username)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
krb5_ntlm_req_set_targetname(krb5_context context,krb5_ntlm ntlm,const char * targetname)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
krb5_ntlm_req_set_lm(krb5_context context,krb5_ntlm ntlm,void * hash,size_t len)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
krb5_ntlm_req_set_ntlm(krb5_context context,krb5_ntlm ntlm,void * hash,size_t len)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
krb5_ntlm_req_set_opaque(krb5_context context,krb5_ntlm ntlm,krb5_data * opaque)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
krb5_ntlm_req_set_session(krb5_context context,krb5_ntlm ntlm,void * sessionkey,size_t length)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
krb5_ntlm_rep_get_status(krb5_context context,krb5_ntlm ntlm)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
krb5_ntlm_rep_get_sessionkey(krb5_context context,krb5_ntlm ntlm,krb5_data * data)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
krb5_digest_probe(krb5_context context,krb5_realm realm,krb5_ccache ccache,unsigned * flags)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