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