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