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