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"
35RCSID("$Id: digest.c 22156 2007-12-04 20:02:49Z lha $");
36#include "digest_asn1.h"
37
38struct krb5_digest_data {
39    char *cbtype;
40    char *cbbinding;
41
42    DigestInit init;
43    DigestInitReply initReply;
44    DigestRequest request;
45    DigestResponse response;
46};
47
48krb5_error_code
49krb5_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
64void
65krb5_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
78krb5_error_code
79krb5_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;
100error:
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
111krb5_error_code
112krb5_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
128krb5_error_code
129krb5_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
152const char *
153krb5_digest_get_server_nonce(krb5_context context,
154			     krb5_digest digest)
155{
156    return digest->initReply.nonce;
157}
158
159krb5_error_code
160krb5_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
176const char *
177krb5_digest_get_opaque(krb5_context context,
178		       krb5_digest digest)
179{
180    return digest->initReply.opaque;
181}
182
183krb5_error_code
184krb5_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
200const char *
201krb5_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
209krb5_error_code
210krb5_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
233static krb5_error_code
234digest_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
382out:
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
403krb5_error_code
404krb5_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
448out:
449    free_DigestRepInner(&irep);
450
451    return ret;
452}
453
454
455krb5_error_code
456krb5_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
480krb5_error_code
481krb5_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
497krb5_error_code
498krb5_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
514krb5_error_code
515krb5_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
538krb5_error_code
539krb5_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
559krb5_error_code
560krb5_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
583krb5_error_code
584krb5_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
607krb5_error_code
608krb5_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
631krb5_error_code
632krb5_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
656krb5_error_code
657krb5_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
680int
681krb5_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
693krb5_error_code
694krb5_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
744out:
745    free_DigestRepInner(&irep);
746
747    return ret;
748}
749
750krb5_boolean
751krb5_digest_rep_get_status(krb5_context context,
752			   krb5_digest digest)
753{
754    return digest->response.success ? TRUE : FALSE;
755}
756
757const char *
758krb5_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
766krb5_error_code
767krb5_digest_get_tickets(krb5_context context,
768			krb5_digest digest,
769			Ticket **tickets)
770{
771    *tickets = NULL;
772    return 0;
773}
774
775
776krb5_error_code
777krb5_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
798krb5_error_code
799krb5_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
815struct krb5_ntlm_data {
816    NTLMInit init;
817    NTLMInitReply initReply;
818    NTLMRequest request;
819    NTLMResponse response;
820};
821
822krb5_error_code
823krb5_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
834krb5_error_code
835krb5_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
847krb5_error_code
848krb5_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
900out:
901    free_DigestRepInner(&irep);
902
903    return ret;
904}
905
906krb5_error_code
907krb5_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
915krb5_error_code
916krb5_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
929krb5_error_code
930krb5_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
943krb5_error_code
944krb5_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
956krb5_error_code
957krb5_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
979krb5_error_code
980krb5_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
1019out:
1020    free_DigestRepInner(&irep);
1021
1022    return ret;
1023}
1024
1025krb5_error_code
1026krb5_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
1034krb5_error_code
1035krb5_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
1047krb5_error_code
1048krb5_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
1060krb5_error_code
1061krb5_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
1075krb5_error_code
1076krb5_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
1090krb5_error_code
1091krb5_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
1105krb5_error_code
1106krb5_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
1125krb5_boolean
1126krb5_ntlm_rep_get_status(krb5_context context,
1127			 krb5_ntlm ntlm)
1128{
1129    return ntlm->response.success ? TRUE : FALSE;
1130}
1131
1132krb5_error_code
1133krb5_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
1160krb5_error_code
1161krb5_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
1195out:
1196    free_DigestRepInner(&irep);
1197
1198    return ret;
1199}
1200