1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* kadmin/server/auth.c - kadm5_auth pluggable interface consumer */ 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 #include "k5-int.h" 34 #include <kadm5/admin.h> 35 #include <krb5/kadm5_auth_plugin.h> 36 #include "auth.h" 37 38 typedef struct { 39 struct kadm5_auth_vtable_st vt; 40 kadm5_auth_moddata data; 41 } *auth_handle; 42 43 static auth_handle *handles; 44 45 void 46 auth_fini(krb5_context context) 47 { 48 auth_handle *hp, h; 49 50 if (handles == NULL) 51 return; 52 for (hp = handles; *hp != NULL; hp++) { 53 h = *hp; 54 if (h->vt.fini != NULL) 55 h->vt.fini(context, h->data); 56 free(h); 57 } 58 free(handles); 59 handles = NULL; 60 } 61 62 krb5_error_code 63 auth_init(krb5_context context, const char *acl_file) 64 { 65 krb5_error_code ret; 66 krb5_plugin_initvt_fn *modules = NULL, *mod; 67 size_t count; 68 auth_handle h = NULL; 69 const int intf = PLUGIN_INTERFACE_KADM5_AUTH; 70 71 ret = k5_plugin_register(context, intf, "acl", kadm5_auth_acl_initvt); 72 if (ret) 73 goto cleanup; 74 ret = k5_plugin_register(context, intf, "self", kadm5_auth_self_initvt); 75 if (ret) 76 goto cleanup; 77 ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_KADM5_AUTH, &modules); 78 if (ret) 79 goto cleanup; 80 81 /* Allocate a large enough list of handles. */ 82 for (count = 0; modules[count] != NULL; count++); 83 handles = k5calloc(count + 1, sizeof(*handles), &ret); 84 if (handles == NULL) 85 goto cleanup; 86 87 /* For each module, allocate a handle, initialize its vtable, and 88 * initialize its module data. */ 89 count = 0; 90 for (mod = modules; *mod != NULL; mod++) { 91 h = k5alloc(sizeof(*h), &ret); 92 if (h == NULL) 93 goto cleanup; 94 ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&h->vt); 95 if (ret) { /* Failed vtable init is non-fatal. */ 96 TRACE_KADM5_AUTH_VTINIT_FAIL(context, ret); 97 free(h); 98 h = NULL; 99 continue; 100 } 101 h->data = NULL; 102 if (h->vt.init != NULL) { 103 ret = h->vt.init(context, acl_file, &h->data); 104 if (ret == KRB5_PLUGIN_NO_HANDLE) { 105 TRACE_KADM5_AUTH_INIT_SKIP(context, h->vt.name); 106 free(h); 107 h = NULL; 108 continue; 109 } 110 if (ret) { 111 TRACE_KADM5_AUTH_INIT_FAIL(context, h->vt.name, ret); 112 goto cleanup; 113 } 114 } 115 handles[count++] = h; 116 handles[count] = NULL; 117 h = NULL; 118 } 119 120 ret = 0; 121 122 cleanup: 123 if (ret) 124 auth_fini(context); 125 free(h); 126 k5_plugin_free_modules(context, modules); 127 return ret; 128 } 129 130 /* Invoke the appropriate method from h->vt for opcode, passing client and the 131 * correct subset of p1, p2, s1, s2, polent, and mask for the method. */ 132 static krb5_error_code 133 call_module(krb5_context context, auth_handle h, int opcode, 134 krb5_const_principal client, krb5_const_principal p1, 135 krb5_const_principal p2, const char *s1, const char *s2, 136 const kadm5_policy_ent_rec *polent, long mask) 137 { 138 /* addprinc and modprinc are handled through auth_restrict(). */ 139 assert(opcode != OP_ADDPRINC && opcode != OP_MODPRINC); 140 141 if (opcode == OP_SETSTR && h->vt.setstr != NULL) 142 return h->vt.setstr(context, h->data, client, p1, s1, s2); 143 else if (opcode == OP_CPW && h->vt.cpw != NULL) 144 return h->vt.cpw(context, h->data, client, p1); 145 else if (opcode == OP_CHRAND && h->vt.chrand != NULL) 146 return h->vt.chrand(context, h->data, client, p1); 147 else if (opcode == OP_SETKEY && h->vt.setkey != NULL) 148 return h->vt.setkey(context, h->data, client, p1); 149 else if (opcode == OP_PURGEKEYS && h->vt.purgekeys != NULL) 150 return h->vt.purgekeys(context, h->data, client, p1); 151 else if (opcode == OP_DELPRINC && h->vt.delprinc != NULL) 152 return h->vt.delprinc(context, h->data, client, p1); 153 else if (opcode == OP_RENPRINC && h->vt.renprinc != NULL) 154 return h->vt.renprinc(context, h->data, client, p1, p2); 155 else if (opcode == OP_GETPRINC && h->vt.getprinc != NULL) 156 return h->vt.getprinc(context, h->data, client, p1); 157 else if (opcode == OP_GETSTRS && h->vt.getstrs != NULL) 158 return h->vt.getstrs(context, h->data, client, p1); 159 else if (opcode == OP_EXTRACT && h->vt.extract != NULL) 160 return h->vt.extract(context, h->data, client, p1); 161 else if (opcode == OP_LISTPRINCS && h->vt.listprincs != NULL) 162 return h->vt.listprincs(context, h->data, client); 163 else if (opcode == OP_ADDPOL && h->vt.addpol != NULL) 164 return h->vt.addpol(context, h->data, client, s1, polent, mask); 165 else if (opcode == OP_MODPOL && h->vt.modpol != NULL) 166 return h->vt.modpol(context, h->data, client, s1, polent, mask); 167 else if (opcode == OP_DELPOL && h->vt.delpol != NULL) 168 return h->vt.delpol(context, h->data, client, s1); 169 else if (opcode == OP_GETPOL && h->vt.getpol != NULL) 170 return h->vt.getpol(context, h->data, client, s1, s2); 171 else if (opcode == OP_LISTPOLS && h->vt.listpols != NULL) 172 return h->vt.listpols(context, h->data, client); 173 else if (opcode == OP_IPROP && h->vt.iprop != NULL) 174 return h->vt.iprop(context, h->data, client); 175 176 return KRB5_PLUGIN_NO_HANDLE; 177 } 178 179 krb5_boolean 180 auth(krb5_context context, int opcode, krb5_const_principal client, 181 krb5_const_principal p1, krb5_const_principal p2, const char *s1, 182 const char *s2, const kadm5_policy_ent_rec *polent, long mask) 183 { 184 krb5_error_code ret; 185 krb5_boolean authorized = FALSE; 186 auth_handle *hp, h; 187 188 for (hp = handles; *hp != NULL; hp++) { 189 h = *hp; 190 191 ret = call_module(context, h, opcode, client, p1, p2, s1, s2, 192 polent, mask); 193 if (!ret) 194 authorized = TRUE; 195 else if (ret != KRB5_PLUGIN_NO_HANDLE) 196 return FALSE; 197 } 198 199 return authorized; 200 } 201 202 /* Impose restrictions, modifying *ent and *mask. */ 203 static krb5_error_code 204 impose_restrictions(krb5_context context, 205 const struct kadm5_auth_restrictions *rs, 206 kadm5_principal_ent_t ent, long *mask) 207 { 208 krb5_error_code ret; 209 krb5_timestamp now; 210 211 if (rs == NULL) 212 return 0; 213 if (rs->mask & (KADM5_PRINC_EXPIRE_TIME | KADM5_PW_EXPIRATION)) { 214 ret = krb5_timeofday(context, &now); 215 if (ret) 216 return ret; 217 } 218 219 if (rs->mask & KADM5_ATTRIBUTES) { 220 ent->attributes |= rs->require_attrs; 221 ent->attributes &= rs->forbid_attrs; 222 *mask |= KADM5_ATTRIBUTES; 223 } 224 if (rs->mask & KADM5_POLICY_CLR) { 225 *mask &= ~KADM5_POLICY; 226 *mask |= KADM5_POLICY_CLR; 227 } else if (rs->mask & KADM5_POLICY) { 228 if (ent->policy != NULL && strcmp(ent->policy, rs->policy) != 0) { 229 free(ent->policy); 230 ent->policy = NULL; 231 } 232 if (ent->policy == NULL) { 233 ent->policy = strdup(rs->policy); 234 if (ent->policy == NULL) 235 return ENOMEM; 236 } 237 *mask |= KADM5_POLICY; 238 } 239 if (rs->mask & KADM5_PRINC_EXPIRE_TIME) { 240 if (!(*mask & KADM5_PRINC_EXPIRE_TIME) || 241 ts_after(ent->princ_expire_time, ts_incr(now, rs->princ_lifetime))) 242 ent->princ_expire_time = now + rs->princ_lifetime; 243 *mask |= KADM5_PRINC_EXPIRE_TIME; 244 } 245 if (rs->mask & KADM5_PW_EXPIRATION) { 246 if (!(*mask & KADM5_PW_EXPIRATION) || 247 ts_after(ent->pw_expiration, ts_incr(now, rs->pw_lifetime))) 248 ent->pw_expiration = now + rs->pw_lifetime; 249 *mask |= KADM5_PW_EXPIRATION; 250 } 251 if (rs->mask & KADM5_MAX_LIFE) { 252 if (!(*mask & KADM5_MAX_LIFE) || ent->max_life > rs->max_life) 253 ent->max_life = rs->max_life; 254 *mask |= KADM5_MAX_LIFE; 255 } 256 if (rs->mask & KADM5_MAX_RLIFE) { 257 if (!(*mask & KADM5_MAX_RLIFE) || 258 ent->max_renewable_life > rs->max_renewable_life) 259 ent->max_renewable_life = rs->max_renewable_life; 260 *mask |= KADM5_MAX_RLIFE; 261 } 262 return 0; 263 } 264 265 krb5_boolean 266 auth_restrict(krb5_context context, int opcode, krb5_const_principal client, 267 kadm5_principal_ent_t ent, long *mask) 268 { 269 auth_handle *hp, h; 270 krb5_boolean authorized = FALSE; 271 krb5_error_code ret, rs_ret; 272 krb5_const_principal target = ent->principal; 273 struct kadm5_auth_restrictions *rs; 274 275 assert(opcode == OP_ADDPRINC || opcode == OP_MODPRINC); 276 for (hp = handles; *hp != NULL; hp++) { 277 h = *hp; 278 279 ret = KRB5_PLUGIN_NO_HANDLE; 280 rs = NULL; 281 if (opcode == OP_ADDPRINC && h->vt.addprinc != NULL) { 282 ret = h->vt.addprinc(context, h->data, client, target, ent, *mask, 283 &rs); 284 } else if (opcode == OP_MODPRINC && h->vt.modprinc != NULL) { 285 ret = h->vt.modprinc(context, h->data, client, target, ent, *mask, 286 &rs); 287 } 288 if (rs != NULL) { 289 rs_ret = impose_restrictions(context, rs, ent, mask); 290 if (h->vt.free_restrictions != NULL) 291 h->vt.free_restrictions(context, h->data, rs); 292 if (rs_ret) 293 return FALSE; 294 } 295 if (!ret) 296 authorized = TRUE; 297 else if (ret != KRB5_PLUGIN_NO_HANDLE) 298 return FALSE; 299 } 300 301 return authorized; 302 } 303 304 void 305 auth_end(krb5_context context) 306 { 307 auth_handle *hp, h; 308 309 for (hp = handles; *hp != NULL; hp++) { 310 h = *hp; 311 if (h->vt.end != NULL) 312 h->vt.end(context, h->data); 313 } 314 } 315