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