1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* plugins/certauth/main.c - certauth plugin test modules. */ 3 /* 4 * Copyright (C) 2017 by Red Hat, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 * OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <k5-int.h> 34 #include <kdb.h> 35 #include "krb5/certauth_plugin.h" 36 37 struct krb5_certauth_moddata_st { 38 int initialized; 39 }; 40 41 /* Test module 1 passes with an indicator. */ 42 static krb5_error_code 43 test1_authorize(krb5_context context, krb5_certauth_moddata moddata, 44 const uint8_t *cert, size_t cert_len, 45 krb5_const_principal princ, const void *opts, 46 const struct _krb5_db_entry_new *db_entry, 47 char ***authinds_out) 48 { 49 char **ais = NULL; 50 51 ais = calloc(2, sizeof(*ais)); 52 assert(ais != NULL); 53 ais[0] = strdup("test1"); 54 assert(ais[0] != NULL); 55 *authinds_out = ais; 56 return KRB5_PLUGIN_NO_HANDLE; 57 } 58 59 static void 60 test_free_ind(krb5_context context, krb5_certauth_moddata moddata, 61 char **authinds) 62 { 63 size_t i; 64 65 if (authinds == NULL) 66 return; 67 for (i = 0; authinds[i] != NULL; i++) 68 free(authinds[i]); 69 free(authinds); 70 } 71 72 /* A basic moddata test. */ 73 static krb5_error_code 74 test2_init(krb5_context context, krb5_certauth_moddata *moddata_out) 75 { 76 krb5_certauth_moddata mod; 77 78 mod = calloc(1, sizeof(*mod)); 79 assert(mod != NULL); 80 mod->initialized = 1; 81 *moddata_out = mod; 82 return 0; 83 } 84 85 static void 86 test2_fini(krb5_context context, krb5_certauth_moddata moddata) 87 { 88 free(moddata); 89 } 90 91 /* Return true if cert appears to contain the CN name, based on a search of the 92 * DER encoding. */ 93 static krb5_boolean 94 has_cn(krb5_context context, const uint8_t *cert, size_t cert_len, 95 const char *name) 96 { 97 krb5_boolean match = FALSE; 98 uint8_t name_len, cntag[5] = "\x06\x03\x55\x04\x03"; 99 const uint8_t *c; 100 struct k5buf buf; 101 size_t c_left; 102 103 /* Construct a DER search string of the CN AttributeType encoding followed 104 * by a UTF8String encoding containing name as the AttributeValue. */ 105 k5_buf_init_dynamic(&buf); 106 k5_buf_add_len(&buf, cntag, sizeof(cntag)); 107 k5_buf_add(&buf, "\x0C"); 108 assert(strlen(name) < 128); 109 name_len = strlen(name); 110 k5_buf_add_len(&buf, &name_len, 1); 111 k5_buf_add_len(&buf, name, name_len); 112 assert(k5_buf_status(&buf) == 0); 113 114 /* Check for the CN needle in the certificate haystack. */ 115 c_left = cert_len; 116 c = memchr(cert, *cntag, c_left); 117 while (c != NULL) { 118 c_left = cert_len - (c - cert); 119 if (buf.len > c_left) 120 break; 121 if (memcmp(c, buf.data, buf.len) == 0) { 122 match = TRUE; 123 break; 124 } 125 assert(c_left >= 1); 126 c = memchr(c + 1, *cntag, c_left - 1); 127 } 128 129 k5_buf_free(&buf); 130 return match; 131 } 132 133 /* 134 * Test module 2 passes if the principal name is "nocert". Otherwise it 135 * returns OK if the CN of the cert matches the principal name, with indicators 136 * of the module name and princ, and errors if the certificate does not match. 137 */ 138 static krb5_error_code 139 test2_authorize(krb5_context context, krb5_certauth_moddata moddata, 140 const uint8_t *cert, size_t cert_len, 141 krb5_const_principal princ, const void *opts, 142 const struct _krb5_db_entry_new *db_entry, 143 char ***authinds_out) 144 { 145 krb5_error_code ret; 146 char *name = NULL, **ais = NULL; 147 148 *authinds_out = NULL; 149 150 assert(moddata != NULL && moddata->initialized); 151 152 ret = krb5_unparse_name_flags(context, princ, 153 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name); 154 if (ret) 155 goto cleanup; 156 if (strcmp(name, "nocert") == 0) { 157 ret = KRB5_PLUGIN_NO_HANDLE; 158 goto cleanup; 159 } 160 161 if (!has_cn(context, cert, cert_len, name)) { 162 ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH; 163 goto cleanup; 164 } 165 166 /* Create an indicator list with the module name and CN. */ 167 ais = calloc(3, sizeof(*ais)); 168 assert(ais != NULL); 169 ais[0] = strdup("test2"); 170 ais[1] = strdup(name); 171 assert(ais[0] != NULL && ais[1] != NULL); 172 *authinds_out = ais; 173 174 ais = NULL; 175 176 cleanup: 177 krb5_free_unparsed_name(context, name); 178 return ret; 179 } 180 181 /* 182 * Test module 3 reads the "hwauth" string attribute on db_entry, and adds an 183 * authentication indicator of "hwauth:<value>" if the attribute is set. It 184 * returns KRB5_CERTAUTH_HWAUTH if the value is "ok", and 185 * KRB5_CERTAUTH_HWAUTH_PASS if the value is "pass". Otherwise it passes. 186 */ 187 static krb5_error_code 188 test3_authorize(krb5_context context, krb5_certauth_moddata moddata, 189 const uint8_t *cert, size_t cert_len, 190 krb5_const_principal princ, const void *opts, 191 const struct _krb5_db_entry_new *db_entry, 192 char ***authinds_out) 193 { 194 krb5_error_code ret; 195 char *strval = NULL, **ais = NULL; 196 197 ret = krb5_dbe_get_string(context, (krb5_db_entry *)db_entry, "hwauth", 198 &strval); 199 if (ret) 200 return ret; 201 if (strval != NULL && strcmp(strval, "ok") == 0) 202 ret = KRB5_CERTAUTH_HWAUTH; 203 else if (strval != NULL && strcmp(strval, "pass") == 0) 204 ret = KRB5_CERTAUTH_HWAUTH_PASS; 205 else 206 ret = KRB5_PLUGIN_NO_HANDLE; 207 208 if (strval != NULL) { 209 ais = calloc(2, sizeof(*ais)); 210 assert(ais != NULL); 211 if (asprintf(&ais[0], "hwauth:%s", strval) < 0) 212 abort(); 213 assert(ais[0] != NULL); 214 } 215 216 *authinds_out = ais; 217 ais = NULL; 218 219 krb5_dbe_free_string(context, strval); 220 return ret; 221 } 222 223 krb5_error_code 224 certauth_test1_initvt(krb5_context context, int maj_ver, int min_ver, 225 krb5_plugin_vtable vtable); 226 krb5_error_code 227 certauth_test1_initvt(krb5_context context, int maj_ver, int min_ver, 228 krb5_plugin_vtable vtable) 229 { 230 krb5_certauth_vtable vt; 231 232 if (maj_ver != 1) 233 return KRB5_PLUGIN_VER_NOTSUPP; 234 vt = (krb5_certauth_vtable)vtable; 235 vt->name = "test1"; 236 vt->authorize = test1_authorize; 237 vt->free_ind = test_free_ind; 238 return 0; 239 } 240 241 krb5_error_code 242 certauth_test2_initvt(krb5_context context, int maj_ver, int min_ver, 243 krb5_plugin_vtable vtable); 244 krb5_error_code 245 certauth_test2_initvt(krb5_context context, int maj_ver, int min_ver, 246 krb5_plugin_vtable vtable) 247 { 248 krb5_certauth_vtable vt; 249 250 if (maj_ver != 1) 251 return KRB5_PLUGIN_VER_NOTSUPP; 252 vt = (krb5_certauth_vtable)vtable; 253 vt->name = "test2"; 254 vt->authorize = test2_authorize; 255 vt->init = test2_init; 256 vt->fini = test2_fini; 257 vt->free_ind = test_free_ind; 258 return 0; 259 } 260 261 krb5_error_code 262 certauth_test3_initvt(krb5_context context, int maj_ver, int min_ver, 263 krb5_plugin_vtable vtable); 264 265 krb5_error_code 266 certauth_test3_initvt(krb5_context context, int maj_ver, int min_ver, 267 krb5_plugin_vtable vtable) 268 { 269 krb5_certauth_vtable vt; 270 271 if (maj_ver != 1) 272 return KRB5_PLUGIN_VER_NOTSUPP; 273 vt = (krb5_certauth_vtable)vtable; 274 vt->name = "test3"; 275 vt->authorize = test3_authorize; 276 vt->free_ind = test_free_ind; 277 return 0; 278 } 279