2 * Copyright (c) 2019 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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
35 * This plugin authorizes requested certificate SANs and EKUs by checking for
36 * existence of files of the form:
39 * /<path>/<princ>/<ext>-<value>
41 * where <path> is the value of:
43 * [kdc] simple_csr_authorizer_directory = PATH
45 * <princ> is a requesting client principal name with all characters other than
46 * alphanumeric, '-', '_', and non-leading '.' URL-encoded.
57 * and <value> is a display form of the SAN or EKU OID, with SANs URL-encoded
58 * just like principal names (see above).
60 * OIDs are of the form "1.2.3.4.5".
62 * Only digitalSignature and nonRepudiation key usage values are permitted.
66 #include <sys/types.h>
79 #include <common_plugin.h>
80 #include <csr_authorizer_plugin.h>
83 * string_encode_sz() and string_encode() encode a string to be safe for use as
84 * a file name. They function very much like URL encoders, but '~' also gets
85 * encoded, and '@', '-', '_', and non-leading '.' do not.
87 * A corresponding decoder is not needed.
90 string_encode_sz(const char *in)
92 size_t sz = strlen(in);
117 string_encode(const char *in)
119 size_t len = strlen(in);
120 size_t sz = string_encode_sz(in);
125 if ((s = malloc(sz + 1)) == NULL)
129 for (i = k = 0; i < len; i++, first = 0) {
130 unsigned char c = ((const unsigned char *)in)[i];
141 s[k++] = "0123456789abcdef"[(c&0xff)>>4];
142 s[k++] = "0123456789abcdef"[(c&0x0f)];
152 s[k++] = "0123456789abcdef"[(c&0xff)>>4];
153 s[k++] = "0123456789abcdef"[(c&0x0f)];
160 static KRB5_LIB_CALL krb5_error_code
162 krb5_context context,
165 krb5_const_principal client,
166 krb5_boolean *result)
169 hx509_context hx509ctx = NULL;
176 if ((d = krb5_config_get_string(context, NULL, app ? app : "kdc",
177 "simple_csr_authorizer_directory",
179 return KRB5_PLUGIN_NO_HANDLE;
181 if ((ret = hx509_context_init(&hx509ctx)))
184 if ((ret = krb5_unparse_name(context, client, &princ)))
187 s = string_encode(princ);
196 for (i = 0; ret == 0; i++) {
197 hx509_san_type san_type;
203 ret = hx509_request_get_san(csr, i, &san_type, &s);
207 case HX509_SAN_TYPE_EMAIL:
210 case HX509_SAN_TYPE_DNSNAME:
213 case HX509_SAN_TYPE_XMPP:
216 case HX509_SAN_TYPE_PKINIT:
219 case HX509_SAN_TYPE_MS_UPN:
229 if ((san = string_encode(s)) == NULL ||
230 asprintf(&p, "%s/%s/%s-%s", d, princ, prefix, san) == -1 ||
233 ret = stat(p, &st) == -1 ? errno : 0;
240 ret = hx509_request_authorize_san(csr, i);
242 if (ret == HX509_NO_ITEM)
247 for (i = 0; ret == 0; i++) {
251 ret = hx509_request_get_eku(csr, i, &s);
254 if (asprintf(&p, "%s/%s/eku-%s", d, princ, s) == -1 || p == NULL) {
258 ret = stat(p, &st) == -1 ? errno : 0;
264 ret = hx509_request_authorize_eku(csr, i);
266 if (ret == HX509_NO_ITEM)
271 ku = int2KeyUsage(0);
272 ku.digitalSignature = 1;
273 ku.nonRepudiation = 1;
274 hx509_request_authorize_ku(csr, ku);
281 /* Allow another plugin to get a crack at this */
282 ret = KRB5_PLUGIN_NO_HANDLE;
286 ret = krb5_enomem(context);
290 hx509_context_free(&hx509ctx);
296 static KRB5_LIB_CALL krb5_error_code
297 simple_csr_authorizer_init(krb5_context context, void **c)
303 static KRB5_LIB_CALL void
304 simple_csr_authorizer_fini(void *c)
308 static krb5plugin_csr_authorizer_ftable plug_desc =
309 { 1, simple_csr_authorizer_init, simple_csr_authorizer_fini, authorize };
311 static krb5plugin_csr_authorizer_ftable *plugs[] = { &plug_desc };
314 simple_csr_authorizer_get_instance(const char *libname)
316 if (strcmp(libname, "krb5") == 0)
317 return krb5_get_instance(libname);
318 if (strcmp(libname, "kdc") == 0)
319 return kdc_get_instance(libname);
320 if (strcmp(libname, "hx509") == 0)
321 return hx509_get_instance(libname);
325 krb5_plugin_load_ft kdc_csr_authorizer_plugin_load;
327 krb5_error_code KRB5_CALLCONV
328 kdc_csr_authorizer_plugin_load(heim_pcontext context,
329 krb5_get_instance_func_t *get_instance,
331 krb5_plugin_common_ftable_cp **plugins)
333 *get_instance = simple_csr_authorizer_get_instance;
334 *num_plugins = sizeof(plugs) / sizeof(plugs[0]);
335 *plugins = (krb5_plugin_common_ftable_cp *)plugs;