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
krb5_digest_alloc(krb5_context context,krb5_digest * digest)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
krb5_digest_free(krb5_digest digest)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
krb5_digest_set_server_cb(krb5_context context,krb5_digest digest,const char * type,const char * binding)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
krb5_digest_set_type(krb5_context context,krb5_digest digest,const char * type)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
krb5_digest_set_hostname(krb5_context context,krb5_digest digest,const char * hostname)129 krb5_digest_set_hostname(krb5_context context,
130 krb5_digest digest,
131 const char *hostname)
132 {
133 if (digest->init.hostname) {
134 krb5_set_error_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 *
krb5_digest_get_server_nonce(krb5_context context,krb5_digest digest)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
krb5_digest_set_server_nonce(krb5_context context,krb5_digest digest,const char * nonce)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 *
krb5_digest_get_opaque(krb5_context context,krb5_digest digest)177 krb5_digest_get_opaque(krb5_context context,
178 krb5_digest digest)
179 {
180 return digest->initReply.opaque;
181 }
182
183 krb5_error_code
krb5_digest_set_opaque(krb5_context context,krb5_digest digest,const char * opaque)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 *
krb5_digest_get_identifier(krb5_context context,krb5_digest digest)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
krb5_digest_set_identifier(krb5_context context,krb5_digest digest,const char * id)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
digest_request(krb5_context context,krb5_realm realm,krb5_ccache ccache,krb5_key_usage usage,const DigestReqInner * ireq,DigestRepInner * irep)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
krb5_digest_init_request(krb5_context context,krb5_digest digest,krb5_realm realm,krb5_ccache ccache)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
krb5_digest_set_client_nonce(krb5_context context,krb5_digest digest,const char * nonce)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
krb5_digest_set_digest(krb5_context context,krb5_digest digest,const char * dgst)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
krb5_digest_set_username(krb5_context context,krb5_digest digest,const char * username)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
krb5_digest_set_authid(krb5_context context,krb5_digest digest,const char * authid)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
krb5_digest_set_authentication_user(krb5_context context,krb5_digest digest,krb5_principal authentication_user)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
krb5_digest_set_realm(krb5_context context,krb5_digest digest,const char * realm)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
krb5_digest_set_method(krb5_context context,krb5_digest digest,const char * method)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
krb5_digest_set_uri(krb5_context context,krb5_digest digest,const char * uri)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
krb5_digest_set_nonceCount(krb5_context context,krb5_digest digest,const char * nonce_count)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
krb5_digest_set_qop(krb5_context context,krb5_digest digest,const char * qop)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
krb5_digest_set_responseData(krb5_context context,krb5_digest digest,const char * response)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
krb5_digest_request(krb5_context context,krb5_digest digest,krb5_realm realm,krb5_ccache ccache)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
krb5_digest_rep_get_status(krb5_context context,krb5_digest digest)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 *
krb5_digest_get_rsp(krb5_context context,krb5_digest digest)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
krb5_digest_get_tickets(krb5_context context,krb5_digest digest,Ticket ** tickets)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
krb5_digest_get_client_binding(krb5_context context,krb5_digest digest,char ** type,char ** binding)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
krb5_digest_get_session_key(krb5_context context,krb5_digest digest,krb5_data * data)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
krb5_ntlm_alloc(krb5_context context,krb5_ntlm * ntlm)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
krb5_ntlm_free(krb5_context context,krb5_ntlm ntlm)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
krb5_ntlm_init_request(krb5_context context,krb5_ntlm ntlm,krb5_realm realm,krb5_ccache ccache,uint32_t flags,const char * hostname,const char * domainname)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
krb5_ntlm_init_get_flags(krb5_context context,krb5_ntlm ntlm,uint32_t * flags)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
krb5_ntlm_init_get_challange(krb5_context context,krb5_ntlm ntlm,krb5_data * challange)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
krb5_ntlm_init_get_opaque(krb5_context context,krb5_ntlm ntlm,krb5_data * opaque)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
krb5_ntlm_init_get_targetname(krb5_context context,krb5_ntlm ntlm,char ** name)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
krb5_ntlm_init_get_targetinfo(krb5_context context,krb5_ntlm ntlm,krb5_data * data)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
krb5_ntlm_request(krb5_context context,krb5_ntlm ntlm,krb5_realm realm,krb5_ccache ccache)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
krb5_ntlm_req_set_flags(krb5_context context,krb5_ntlm ntlm,uint32_t flags)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
krb5_ntlm_req_set_username(krb5_context context,krb5_ntlm ntlm,const char * username)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
krb5_ntlm_req_set_targetname(krb5_context context,krb5_ntlm ntlm,const char * targetname)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
krb5_ntlm_req_set_lm(krb5_context context,krb5_ntlm ntlm,void * hash,size_t len)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
krb5_ntlm_req_set_ntlm(krb5_context context,krb5_ntlm ntlm,void * hash,size_t len)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
krb5_ntlm_req_set_opaque(krb5_context context,krb5_ntlm ntlm,krb5_data * opaque)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
krb5_ntlm_req_set_session(krb5_context context,krb5_ntlm ntlm,void * sessionkey,size_t length)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
krb5_ntlm_rep_get_status(krb5_context context,krb5_ntlm ntlm)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
krb5_ntlm_rep_get_sessionkey(krb5_context context,krb5_ntlm ntlm,krb5_data * data)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
krb5_digest_probe(krb5_context context,krb5_realm realm,krb5_ccache ccache,unsigned * flags)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 }
1200