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