etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / kdc / kx509.c
blobecd58a50216601830e97c149d8a361e376bdb81e
1 /* $NetBSD: kx509.c,v 1.1.1.2 2014/04/24 12:45:27 pettai Exp $ */
3 /*
4 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
36 #include "kdc_locl.h"
37 #include <krb5/hex.h>
38 #include <krb5/rfc2459_asn1.h>
39 #include <krb5/hx509.h>
41 #ifdef KX509
47 krb5_error_code
48 _kdc_try_kx509_request(void *ptr, size_t len, struct Kx509Request *req, size_t *size)
50 if (len < 4)
51 return -1;
52 if (memcmp("\x00\x00\x02\x00", ptr, 4) != 0)
53 return -1;
54 return decode_Kx509Request(((unsigned char *)ptr) + 4, len - 4, req, size);
61 static const unsigned char version_2_0[4] = {0 , 0, 2, 0};
63 static krb5_error_code
64 verify_req_hash(krb5_context context,
65 const Kx509Request *req,
66 krb5_keyblock *key)
68 unsigned char digest[SHA_DIGEST_LENGTH];
69 HMAC_CTX ctx;
71 if (req->pk_hash.length != sizeof(digest)) {
72 krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
73 "pk-hash have wrong length: %lu",
74 (unsigned long)req->pk_hash.length);
75 return KRB5KDC_ERR_PREAUTH_FAILED;
78 HMAC_CTX_init(&ctx);
79 HMAC_Init_ex(&ctx,
80 key->keyvalue.data, key->keyvalue.length,
81 EVP_sha1(), NULL);
82 if (sizeof(digest) != HMAC_size(&ctx))
83 krb5_abortx(context, "runtime error, hmac buffer wrong size in kx509");
84 HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
85 HMAC_Update(&ctx, req->pk_key.data, req->pk_key.length);
86 HMAC_Final(&ctx, digest, 0);
87 HMAC_CTX_cleanup(&ctx);
89 if (memcmp(req->pk_hash.data, digest, sizeof(digest)) != 0) {
90 krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
91 "pk-hash is not correct");
92 return KRB5KDC_ERR_PREAUTH_FAILED;
94 return 0;
97 static krb5_error_code
98 calculate_reply_hash(krb5_context context,
99 krb5_keyblock *key,
100 Kx509Response *rep)
102 krb5_error_code ret;
103 HMAC_CTX ctx;
105 HMAC_CTX_init(&ctx);
107 HMAC_Init_ex(&ctx, key->keyvalue.data, key->keyvalue.length,
108 EVP_sha1(), NULL);
109 ret = krb5_data_alloc(rep->hash, HMAC_size(&ctx));
110 if (ret) {
111 HMAC_CTX_cleanup(&ctx);
112 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
113 return ENOMEM;
116 HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
117 if (rep->error_code) {
118 int32_t t = *rep->error_code;
119 do {
120 unsigned char p = (t & 0xff);
121 HMAC_Update(&ctx, &p, 1);
122 t >>= 8;
123 } while (t);
125 if (rep->certificate)
126 HMAC_Update(&ctx, rep->certificate->data, rep->certificate->length);
127 if (rep->e_text)
128 HMAC_Update(&ctx, (unsigned char *)*rep->e_text, strlen(*rep->e_text));
130 HMAC_Final(&ctx, rep->hash->data, 0);
131 HMAC_CTX_cleanup(&ctx);
133 return 0;
137 * Build a certifate for `principal´ that will expire at `endtime´.
140 static krb5_error_code
141 build_certificate(krb5_context context,
142 krb5_kdc_configuration *config,
143 const krb5_data *key,
144 time_t endtime,
145 krb5_principal principal,
146 krb5_data *certificate)
148 hx509_ca_tbs tbs = NULL;
149 hx509_env env = NULL;
150 hx509_cert cert = NULL;
151 hx509_cert signer = NULL;
152 int ret;
154 if (krb5_principal_get_comp_string(context, principal, 1) != NULL) {
155 kdc_log(context, config, 0, "Principal is not a user");
156 return EINVAL;
159 ret = hx509_env_add(context->hx509ctx, &env, "principal-name",
160 krb5_principal_get_comp_string(context, principal, 0));
161 if (ret)
162 goto out;
165 hx509_certs certs;
166 hx509_query *q;
168 ret = hx509_certs_init(context->hx509ctx, config->kx509_ca, 0,
169 NULL, &certs);
170 if (ret) {
171 kdc_log(context, config, 0, "Failed to load CA %s",
172 config->kx509_ca);
173 goto out;
175 ret = hx509_query_alloc(context->hx509ctx, &q);
176 if (ret) {
177 hx509_certs_free(&certs);
178 goto out;
181 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
182 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN);
184 ret = hx509_certs_find(context->hx509ctx, certs, q, &signer);
185 hx509_query_free(context->hx509ctx, q);
186 hx509_certs_free(&certs);
187 if (ret) {
188 kdc_log(context, config, 0, "Failed to find a CA in %s",
189 config->kx509_ca);
190 goto out;
194 ret = hx509_ca_tbs_init(context->hx509ctx, &tbs);
195 if (ret)
196 goto out;
199 SubjectPublicKeyInfo spki;
200 heim_any any;
202 memset(&spki, 0, sizeof(spki));
204 spki.subjectPublicKey.data = key->data;
205 spki.subjectPublicKey.length = key->length * 8;
207 ret = der_copy_oid(&asn1_oid_id_pkcs1_rsaEncryption,
208 &spki.algorithm.algorithm);
210 any.data = "\x05\x00";
211 any.length = 2;
212 spki.algorithm.parameters = &any;
214 ret = hx509_ca_tbs_set_spki(context->hx509ctx, tbs, &spki);
215 der_free_oid(&spki.algorithm.algorithm);
216 if (ret)
217 goto out;
221 hx509_certs certs;
222 hx509_cert template;
224 ret = hx509_certs_init(context->hx509ctx, config->kx509_template, 0,
225 NULL, &certs);
226 if (ret) {
227 kdc_log(context, config, 0, "Failed to load template %s",
228 config->kx509_template);
229 goto out;
231 ret = hx509_get_one_cert(context->hx509ctx, certs, &template);
232 hx509_certs_free(&certs);
233 if (ret) {
234 kdc_log(context, config, 0, "Failed to find template in %s",
235 config->kx509_template);
236 goto out;
238 ret = hx509_ca_tbs_set_template(context->hx509ctx, tbs,
239 HX509_CA_TEMPLATE_SUBJECT|
240 HX509_CA_TEMPLATE_KU|
241 HX509_CA_TEMPLATE_EKU,
242 template);
243 hx509_cert_free(template);
244 if (ret)
245 goto out;
248 hx509_ca_tbs_set_notAfter(context->hx509ctx, tbs, endtime);
250 hx509_ca_tbs_subject_expand(context->hx509ctx, tbs, env);
251 hx509_env_free(&env);
253 ret = hx509_ca_sign(context->hx509ctx, tbs, signer, &cert);
254 hx509_cert_free(signer);
255 if (ret)
256 goto out;
258 hx509_ca_tbs_free(&tbs);
260 ret = hx509_cert_binary(context->hx509ctx, cert, certificate);
261 hx509_cert_free(cert);
262 if (ret)
263 goto out;
265 return 0;
266 out:
267 if (env)
268 hx509_env_free(&env);
269 if (tbs)
270 hx509_ca_tbs_free(&tbs);
271 if (signer)
272 hx509_cert_free(signer);
273 krb5_set_error_message(context, ret, "cert creation failed");
274 return ret;
281 krb5_error_code
282 _kdc_do_kx509(krb5_context context,
283 krb5_kdc_configuration *config,
284 const struct Kx509Request *req, krb5_data *reply,
285 const char *from, struct sockaddr *addr)
287 krb5_error_code ret;
288 krb5_ticket *ticket = NULL;
289 krb5_flags ap_req_options;
290 krb5_auth_context ac = NULL;
291 krb5_keytab id = NULL;
292 krb5_principal sprincipal = NULL, cprincipal = NULL;
293 char *cname = NULL;
294 Kx509Response rep;
295 size_t size;
296 krb5_keyblock *key = NULL;
298 krb5_data_zero(reply);
299 memset(&rep, 0, sizeof(rep));
301 if(!config->enable_kx509) {
302 kdc_log(context, config, 0,
303 "Rejected kx509 request (disabled) from %s", from);
304 return KRB5KDC_ERR_POLICY;
307 kdc_log(context, config, 0, "Kx509 request from %s", from);
309 ret = krb5_kt_resolve(context, "HDB:", &id);
310 if (ret) {
311 kdc_log(context, config, 0, "Can't open database for digest");
312 goto out;
315 ret = krb5_rd_req(context,
316 &ac,
317 &req->authenticator,
318 NULL,
320 &ap_req_options,
321 &ticket);
322 if (ret)
323 goto out;
325 ret = krb5_ticket_get_client(context, ticket, &cprincipal);
326 if (ret)
327 goto out;
329 ret = krb5_unparse_name(context, cprincipal, &cname);
330 if (ret)
331 goto out;
333 /* verify server principal */
335 ret = krb5_sname_to_principal(context, NULL, "kca_service",
336 KRB5_NT_UNKNOWN, &sprincipal);
337 if (ret)
338 goto out;
341 krb5_principal principal = NULL;
343 ret = krb5_ticket_get_server(context, ticket, &principal);
344 if (ret)
345 goto out;
347 ret = krb5_principal_compare(context, sprincipal, principal);
348 krb5_free_principal(context, principal);
349 if (ret != TRUE) {
350 char *expected, *used;
352 ret = krb5_unparse_name(context, sprincipal, &expected);
353 if (ret)
354 goto out;
355 ret = krb5_unparse_name(context, principal, &used);
356 if (ret) {
357 krb5_xfree(expected);
358 goto out;
361 ret = KRB5KDC_ERR_SERVER_NOMATCH;
362 krb5_set_error_message(context, ret,
363 "User %s used wrong Kx509 service "
364 "principal, expected: %s, used %s",
365 cname, expected, used);
366 krb5_xfree(expected);
367 krb5_xfree(used);
368 goto out;
372 ret = krb5_auth_con_getkey(context, ac, &key);
373 if (ret == 0 && key == NULL)
374 ret = KRB5KDC_ERR_NULL_KEY;
375 if (ret) {
376 krb5_set_error_message(context, ret, "Kx509 can't get session key");
377 goto out;
380 ret = verify_req_hash(context, req, key);
381 if (ret)
382 goto out;
384 /* Verify that the key is encoded RSA key */
386 RSAPublicKey key;
387 size_t size;
389 ret = decode_RSAPublicKey(req->pk_key.data, req->pk_key.length,
390 &key, &size);
391 if (ret)
392 goto out;
393 free_RSAPublicKey(&key);
394 if (size != req->pk_key.length) {
395 ret = ASN1_EXTRA_DATA;
396 goto out;
400 ALLOC(rep.certificate);
401 if (rep.certificate == NULL)
402 goto out;
403 krb5_data_zero(rep.certificate);
404 ALLOC(rep.hash);
405 if (rep.hash == NULL)
406 goto out;
407 krb5_data_zero(rep.hash);
409 ret = build_certificate(context, config, &req->pk_key,
410 krb5_ticket_get_endtime(context, ticket),
411 cprincipal, rep.certificate);
412 if (ret)
413 goto out;
415 ret = calculate_reply_hash(context, key, &rep);
416 if (ret)
417 goto out;
420 * Encode reply, [ version | Kx509Response ]
424 krb5_data data;
426 ASN1_MALLOC_ENCODE(Kx509Response, data.data, data.length, &rep,
427 &size, ret);
428 if (ret) {
429 krb5_set_error_message(context, ret, "Failed to encode kx509 reply");
430 goto out;
432 if (size != data.length)
433 krb5_abortx(context, "ASN1 internal error");
435 ret = krb5_data_alloc(reply, data.length + sizeof(version_2_0));
436 if (ret) {
437 free(data.data);
438 goto out;
440 memcpy(reply->data, version_2_0, sizeof(version_2_0));
441 memcpy(((unsigned char *)reply->data) + sizeof(version_2_0),
442 data.data, data.length);
443 free(data.data);
446 kdc_log(context, config, 0, "Successful Kx509 request for %s", cname);
448 out:
449 if (ac)
450 krb5_auth_con_free(context, ac);
451 if (ret)
452 krb5_warn(context, ret, "Kx509 request from %s failed", from);
453 if (ticket)
454 krb5_free_ticket(context, ticket);
455 if (id)
456 krb5_kt_close(context, id);
457 if (sprincipal)
458 krb5_free_principal(context, sprincipal);
459 if (cprincipal)
460 krb5_free_principal(context, cprincipal);
461 if (key)
462 krb5_free_keyblock (context, key);
463 if (cname)
464 free(cname);
465 free_Kx509Response(&rep);
467 return 0;
470 #endif /* KX509 */