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