1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* plugins/kadm5_auth/test/main.c - test modules for kadm5_auth interface */ 3 /* 4 * Copyright (C) 2017 by the Massachusetts Institute of Technology. 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 /* 34 * This file implements two testing kadm5_auth modules, the welcomer and the 35 * bouncer. The welcomer implements permissive behavior, while the bouncer 36 * implements restrictive behavior. 37 * 38 * Module data objects and restrictions are adequately tested by the acl 39 * module, so we do not test them here. Focus instead on the ability to 40 * examine principal and policy objects and to perform DB operations. 41 */ 42 43 #include "k5-int.h" 44 #include <kadm5/admin.h> 45 #include <krb5/kadm5_auth_plugin.h> 46 47 krb5_error_code 48 kadm5_auth_welcomer_initvt(krb5_context context, int maj_ver, int min_ver, 49 krb5_plugin_vtable vtable); 50 krb5_error_code 51 kadm5_auth_bouncer_initvt(krb5_context context, int maj_ver, int min_ver, 52 krb5_plugin_vtable vtable); 53 54 /* The welcomer authorizes all getprinc operations, since kadmin uses them as a 55 * precursor to modprinc. */ 56 static krb5_error_code 57 welcomer_getprinc(krb5_context context, kadm5_auth_moddata data, 58 krb5_const_principal client, krb5_const_principal target) 59 { 60 return 0; 61 } 62 63 /* The welcomer authorizes addprinc operations which set a policy "VIP". */ 64 static krb5_error_code 65 welcomer_addprinc(krb5_context context, kadm5_auth_moddata data, 66 krb5_const_principal client, krb5_const_principal target, 67 const struct _kadm5_principal_ent_t *ent, long mask, 68 struct kadm5_auth_restrictions **rs_out) 69 { 70 if ((mask & KADM5_POLICY) && strcmp(ent->policy, "VIP") == 0) 71 return 0; 72 return KRB5_PLUGIN_NO_HANDLE; 73 } 74 75 /* The bouncer denies addprinc operations which include a maximum lifetime. */ 76 static krb5_error_code 77 bouncer_addprinc(krb5_context context, kadm5_auth_moddata data, 78 krb5_const_principal client, krb5_const_principal target, 79 const struct _kadm5_principal_ent_t *ent, long mask, 80 struct kadm5_auth_restrictions **rs_out) 81 { 82 return (mask & KADM5_MAX_LIFE) ? EPERM : KRB5_PLUGIN_NO_HANDLE; 83 } 84 85 /* The welcomer authorizes modprinc operations which only set maxrenewlife. */ 86 static krb5_error_code 87 welcomer_modprinc(krb5_context context, kadm5_auth_moddata data, 88 krb5_const_principal client, krb5_const_principal target, 89 const struct _kadm5_principal_ent_t *ent, long mask, 90 struct kadm5_auth_restrictions **rs_out) 91 { 92 return (mask == KADM5_MAX_RLIFE) ? 0 : KRB5_PLUGIN_NO_HANDLE; 93 } 94 95 /* The bouncer denies modprinc operations if the target principal has an even 96 * number of components. */ 97 static krb5_error_code 98 bouncer_modprinc(krb5_context context, kadm5_auth_moddata data, 99 krb5_const_principal client, krb5_const_principal target, 100 const struct _kadm5_principal_ent_t *ent, long mask, 101 struct kadm5_auth_restrictions **rs_out) 102 { 103 return (target->length % 2 == 0) ? EPERM : KRB5_PLUGIN_NO_HANDLE; 104 } 105 106 /* The welcomer authorizes setstr operations for the attribute "note". */ 107 static krb5_error_code 108 welcomer_setstr(krb5_context context, kadm5_auth_moddata data, 109 krb5_const_principal client, krb5_const_principal target, 110 const char *key, const char *value) 111 { 112 return (strcmp(key, "note") == 0) ? 0 : KRB5_PLUGIN_NO_HANDLE; 113 } 114 115 /* The bouncer denies setstr operations if the value is more than 10 bytes. */ 116 static krb5_error_code 117 bouncer_setstr(krb5_context context, kadm5_auth_moddata data, 118 krb5_const_principal client, krb5_const_principal target, 119 const char *key, const char *value) 120 { 121 return (strlen(value) > 10) ? EPERM : KRB5_PLUGIN_NO_HANDLE; 122 } 123 124 /* The welcomer authorizes delprinc operations if the target principal starts 125 * with "d". */ 126 static krb5_error_code 127 welcomer_delprinc(krb5_context context, kadm5_auth_moddata data, 128 krb5_const_principal client, krb5_const_principal target) 129 { 130 if (target->length > 0 && target->data[0].length > 0 && 131 *target->data[0].data == 'd') 132 return 0; 133 return KRB5_PLUGIN_NO_HANDLE; 134 } 135 136 /* The bouncer denies delprinc operations if the target principal has the 137 * "nodelete" string attribute. */ 138 static krb5_error_code 139 bouncer_delprinc(krb5_context context, kadm5_auth_moddata data, 140 krb5_const_principal client, krb5_const_principal target) 141 { 142 krb5_error_code ret; 143 krb5_db_entry *ent; 144 char *val = NULL; 145 146 if (krb5_db_get_principal(context, target, 0, &ent) != 0) 147 return EPERM; 148 ret = krb5_dbe_get_string(context, ent, "nodelete", &val); 149 krb5_db_free_principal(context, ent); 150 ret = (ret != 0 || val != NULL) ? EPERM : KRB5_PLUGIN_NO_HANDLE; 151 krb5_dbe_free_string(context, val); 152 return ret; 153 } 154 155 /* The welcomer authorizes rename operations if the first components of the 156 * principals have the same length. */ 157 static krb5_error_code 158 welcomer_renprinc(krb5_context context, kadm5_auth_moddata data, 159 krb5_const_principal client, krb5_const_principal src, 160 krb5_const_principal dest) 161 { 162 if (src->length > 0 && dest->length > 0 && 163 src->data[0].length == dest->data[0].length) 164 return 0; 165 return KRB5_PLUGIN_NO_HANDLE; 166 } 167 168 /* The bouncer denies rename operations if the source principal starts with 169 * "a". */ 170 static krb5_error_code 171 bouncer_renprinc(krb5_context context, kadm5_auth_moddata data, 172 krb5_const_principal client, krb5_const_principal src, 173 krb5_const_principal dest) 174 { 175 if (src->length > 0 && src->data[0].length > 0 && 176 *src->data[0].data == 'a') 177 return EPERM; 178 return KRB5_PLUGIN_NO_HANDLE; 179 } 180 181 /* The welcomer authorizes addpol operations which set a minlength of 3. */ 182 static krb5_error_code 183 welcomer_addpol(krb5_context context, kadm5_auth_moddata data, 184 krb5_const_principal client, const char *policy, 185 const struct _kadm5_policy_ent_t *ent, long mask) 186 { 187 if ((mask & KADM5_PW_MIN_LENGTH) && ent->pw_min_length == 3) 188 return 0; 189 return KRB5_PLUGIN_NO_HANDLE; 190 } 191 192 /* The bouncer denies addpol operations if the name is 3 bytes or less. */ 193 static krb5_error_code 194 bouncer_addpol(krb5_context context, kadm5_auth_moddata data, 195 krb5_const_principal client, const char *policy, 196 const struct _kadm5_policy_ent_t *ent, long mask) 197 { 198 return (strlen(policy) <= 3) ? EPERM : KRB5_PLUGIN_NO_HANDLE; 199 } 200 201 /* The welcomer authorizes modpol operations which only change min_life. */ 202 static krb5_error_code 203 welcomer_modpol(krb5_context context, kadm5_auth_moddata data, 204 krb5_const_principal client, const char *policy, 205 const struct _kadm5_policy_ent_t *ent, long mask) 206 { 207 return (mask == KADM5_PW_MIN_LIFE) ? 0 : KRB5_PLUGIN_NO_HANDLE; 208 } 209 210 /* The bouncer denies modpol operations which set pw_min_life above 10. */ 211 static krb5_error_code 212 bouncer_modpol(krb5_context context, kadm5_auth_moddata data, 213 krb5_const_principal client, const char *policy, 214 const struct _kadm5_policy_ent_t *ent, long mask) 215 { 216 if ((mask & KADM5_PW_MIN_LIFE) && ent->pw_min_life > 10) 217 return EPERM; 218 return KRB5_PLUGIN_NO_HANDLE; 219 } 220 221 /* The welcomer authorizes getpol operations if the policy and client principal 222 * policy have the same length. */ 223 static krb5_error_code 224 welcomer_getpol(krb5_context context, kadm5_auth_moddata data, 225 krb5_const_principal client, const char *policy, 226 const char *client_policy) 227 { 228 if (client_policy != NULL && strlen(policy) == strlen(client_policy)) 229 return 0; 230 return KRB5_PLUGIN_NO_HANDLE; 231 } 232 233 /* The bouncer denies getpol operations if the policy name begins with 'x'. */ 234 static krb5_error_code 235 bouncer_getpol(krb5_context context, kadm5_auth_moddata data, 236 krb5_const_principal client, const char *policy, 237 const char *client_policy) 238 { 239 return (*policy == 'x') ? EPERM : KRB5_PLUGIN_NO_HANDLE; 240 } 241 242 /* The welcomer counts end calls by incrementing the "ends" string attribute on 243 * the "opcount" principal, if it exists. */ 244 static void 245 welcomer_end(krb5_context context, kadm5_auth_moddata data) 246 { 247 krb5_principal princ = NULL; 248 krb5_db_entry *ent = NULL; 249 char *val = NULL, buf[10]; 250 251 if (krb5_parse_name(context, "opcount", &princ) != 0) 252 goto cleanup; 253 if (krb5_db_get_principal(context, princ, 0, &ent) != 0) 254 goto cleanup; 255 if (krb5_dbe_get_string(context, ent, "ends", &val) != 0 || val == NULL) 256 goto cleanup; 257 snprintf(buf, sizeof(buf), "%d", atoi(val) + 1); 258 if (krb5_dbe_set_string(context, ent, "ends", buf) != 0) 259 goto cleanup; 260 ent->mask = KADM5_TL_DATA; 261 krb5_db_put_principal(context, ent); 262 263 cleanup: 264 krb5_dbe_free_string(context, val); 265 krb5_db_free_principal(context, ent); 266 krb5_free_principal(context, princ); 267 } 268 269 krb5_error_code 270 kadm5_auth_welcomer_initvt(krb5_context context, int maj_ver, int min_ver, 271 krb5_plugin_vtable vtable) 272 { 273 kadm5_auth_vtable vt = (kadm5_auth_vtable)vtable; 274 275 vt->name = "welcomer"; 276 vt->addprinc = welcomer_addprinc; 277 vt->modprinc = welcomer_modprinc; 278 vt->setstr = welcomer_setstr; 279 vt->delprinc = welcomer_delprinc; 280 vt->renprinc = welcomer_renprinc; 281 vt->getprinc = welcomer_getprinc; 282 vt->addpol = welcomer_addpol; 283 vt->modpol = welcomer_modpol; 284 vt->getpol = welcomer_getpol; 285 vt->end = welcomer_end; 286 return 0; 287 } 288 289 krb5_error_code 290 kadm5_auth_bouncer_initvt(krb5_context context, int maj_ver, int min_ver, 291 krb5_plugin_vtable vtable) 292 { 293 kadm5_auth_vtable vt = (kadm5_auth_vtable)vtable; 294 295 vt->name = "bouncer"; 296 vt->addprinc = bouncer_addprinc; 297 vt->modprinc = bouncer_modprinc; 298 vt->setstr = bouncer_setstr; 299 vt->delprinc = bouncer_delprinc; 300 vt->renprinc = bouncer_renprinc; 301 vt->addpol = bouncer_addpol; 302 vt->modpol = bouncer_modpol; 303 vt->getpol = bouncer_getpol; 304 return 0; 305 } 306