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
39struct krb5_digest_data {
40    char *cbtype;
41    char *cbbinding;
42
43    DigestInit init;
44    DigestInitReply initReply;
45    DigestRequest request;
46    DigestResponse response;
47};
48
49KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
50krb5_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
65KRB5_LIB_FUNCTION void KRB5_LIB_CALL
66krb5_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
79KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
80krb5_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
113KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
114krb5_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
130KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
131krb5_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
154KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
155krb5_digest_get_server_nonce(krb5_context context,
156			     krb5_digest digest)
157{
158    return digest->initReply.nonce;
159}
160
161KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
162krb5_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
178KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
179krb5_digest_get_opaque(krb5_context context,
180		       krb5_digest digest)
181{
182    return digest->initReply.opaque;
183}
184
185KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
186krb5_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
202KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
203krb5_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
211KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
212krb5_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
235static krb5_error_code
236digest_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
409KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
410krb5_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
464KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
465krb5_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
490KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
491krb5_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
508KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
509krb5_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
525KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
526krb5_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
549KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
550krb5_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
569KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
570krb5_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
593KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
594krb5_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
618KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
619krb5_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
642KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
643krb5_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
668KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
669krb5_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
692KRB5_LIB_FUNCTION int KRB5_LIB_CALL
693krb5_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
705KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
706krb5_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
768KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
769krb5_digest_rep_get_status(krb5_context context,
770			   krb5_digest digest)
771{
772    return digest->response.success ? TRUE : FALSE;
773}
774
775KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
776krb5_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
784KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
785krb5_digest_get_tickets(krb5_context context,
786			krb5_digest digest,
787			Ticket **tickets)
788{
789    *tickets = NULL;
790    return 0;
791}
792
793
794KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
795krb5_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
816KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
817krb5_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
833struct krb5_ntlm_data {
834    NTLMInit init;
835    NTLMInitReply initReply;
836    NTLMRequest request;
837    NTLMResponse response;
838};
839
840KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
841krb5_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
852KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
853krb5_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
865KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
866krb5_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
926KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
927krb5_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
935KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
936krb5_ntlm_init_get_challange(krb5_context context,
937			     krb5_ntlm ntlm,
938			     krb5_data *challenge)
939{
940    krb5_error_code ret;
941
942    ret = der_copy_octet_string(&ntlm->initReply.challenge, challenge);
943    if (ret)
944	krb5_clear_error_message(context);
945
946    return ret;
947}
948
949KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
950krb5_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
963KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
964krb5_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
976KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
977krb5_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
999KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1000krb5_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
1048KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1049krb5_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
1057KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1058krb5_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
1070KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1071krb5_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
1083KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1084krb5_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
1098KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1099krb5_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
1113KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1114krb5_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
1128KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1129krb5_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
1148KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1149krb5_ntlm_rep_get_status(krb5_context context,
1150			 krb5_ntlm ntlm)
1151{
1152    return ntlm->response.success ? TRUE : FALSE;
1153}
1154
1155KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1156krb5_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
1184KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1185krb5_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 */
1226