1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* kdc/policy.c - Policy decision routines for KDC */ 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 "kdc_util.h" 35 #include "extern.h" 36 #include "policy.h" 37 #include "adm_proto.h" 38 #include <krb5/kdcpolicy_plugin.h> 39 #include <syslog.h> 40 41 typedef struct kdcpolicy_handle_st { 42 struct krb5_kdcpolicy_vtable_st vt; 43 krb5_kdcpolicy_moddata moddata; 44 } *kdcpolicy_handle; 45 46 static kdcpolicy_handle *handles; 47 48 static void 49 free_indicators(char **ais) 50 { 51 size_t i; 52 53 if (ais == NULL) 54 return; 55 for (i = 0; ais[i] != NULL; i++) 56 free(ais[i]); 57 free(ais); 58 } 59 60 /* Convert inds to a null-terminated list of C strings. */ 61 static krb5_error_code 62 authind_strings(krb5_data *const *inds, char ***strs_out) 63 { 64 krb5_error_code ret; 65 char **list = NULL; 66 size_t i, count; 67 68 *strs_out = NULL; 69 70 for (count = 0; inds != NULL && inds[count] != NULL; count++); 71 list = k5calloc(count + 1, sizeof(*list), &ret); 72 if (list == NULL) 73 goto error; 74 75 for (i = 0; i < count; i++) { 76 list[i] = k5memdup0(inds[i]->data, inds[i]->length, &ret); 77 if (list[i] == NULL) 78 goto error; 79 } 80 81 *strs_out = list; 82 return 0; 83 84 error: 85 free_indicators(list); 86 return ret; 87 } 88 89 /* Constrain times->endtime to life and times->renew_till to rlife, relative to 90 * now. */ 91 static void 92 update_ticket_times(krb5_ticket_times *times, krb5_timestamp now, 93 krb5_deltat life, krb5_deltat rlife) 94 { 95 if (life) 96 times->endtime = ts_min(ts_incr(now, life), times->endtime); 97 if (rlife) 98 times->renew_till = ts_min(ts_incr(now, rlife), times->renew_till); 99 } 100 101 /* Check an AS request against kdcpolicy modules, updating times with any 102 * module endtime constraints. Set an appropriate status string on error. */ 103 krb5_error_code 104 check_kdcpolicy_as(krb5_context context, const krb5_kdc_req *request, 105 const krb5_db_entry *client, const krb5_db_entry *server, 106 krb5_data *const *auth_indicators, krb5_timestamp kdc_time, 107 krb5_ticket_times *times, const char **status) 108 { 109 krb5_deltat life = 0, rlife = 0; 110 krb5_error_code ret; 111 kdcpolicy_handle *hp, h; 112 char **ais = NULL; 113 114 *status = NULL; 115 116 ret = authind_strings(auth_indicators, &ais); 117 if (ret) 118 goto done; 119 120 for (hp = handles; *hp != NULL; hp++) { 121 h = *hp; 122 if (h->vt.check_as == NULL) 123 continue; 124 125 ret = h->vt.check_as(context, h->moddata, request, client, server, 126 (const char **)ais, status, &life, &rlife); 127 if (ret) 128 goto done; 129 130 update_ticket_times(times, kdc_time, life, rlife); 131 } 132 133 done: 134 free_indicators(ais); 135 return ret; 136 } 137 138 /* 139 * Check the TGS request against the local TGS policy. Accepts an 140 * authentication indicator for the module policy decisions. Returns 0 and a 141 * NULL status string on success. 142 */ 143 krb5_error_code 144 check_kdcpolicy_tgs(krb5_context context, const krb5_kdc_req *request, 145 const krb5_db_entry *server, const krb5_ticket *ticket, 146 krb5_data *const *auth_indicators, krb5_timestamp kdc_time, 147 krb5_ticket_times *times, const char **status) 148 { 149 krb5_deltat life = 0, rlife = 0; 150 krb5_error_code ret; 151 kdcpolicy_handle *hp, h; 152 char **ais = NULL; 153 154 *status = NULL; 155 156 ret = authind_strings(auth_indicators, &ais); 157 if (ret) 158 goto done; 159 160 for (hp = handles; *hp != NULL; hp++) { 161 h = *hp; 162 if (h->vt.check_tgs == NULL) 163 continue; 164 165 ret = h->vt.check_tgs(context, h->moddata, request, server, ticket, 166 (const char **)ais, status, &life, &rlife); 167 if (ret) 168 goto done; 169 170 update_ticket_times(times, kdc_time, life, rlife); 171 } 172 173 done: 174 free_indicators(ais); 175 return ret; 176 } 177 178 void 179 unload_kdcpolicy_plugins(krb5_context context) 180 { 181 kdcpolicy_handle *hp, h; 182 183 for (hp = handles; *hp != NULL; hp++) { 184 h = *hp; 185 if (h->vt.fini != NULL) 186 h->vt.fini(context, h->moddata); 187 free(h); 188 } 189 free(handles); 190 handles = NULL; 191 } 192 193 krb5_error_code 194 load_kdcpolicy_plugins(krb5_context context) 195 { 196 krb5_error_code ret; 197 krb5_plugin_initvt_fn *modules = NULL, *mod; 198 kdcpolicy_handle h; 199 size_t count; 200 201 ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_KDCPOLICY, &modules); 202 if (ret) 203 goto cleanup; 204 205 for (count = 0; modules[count] != NULL; count++); 206 handles = k5calloc(count + 1, sizeof(*handles), &ret); 207 if (handles == NULL) 208 goto cleanup; 209 210 count = 0; 211 for (mod = modules; *mod != NULL; mod++) { 212 h = k5calloc(1, sizeof(*h), &ret); 213 if (h == NULL) 214 goto cleanup; 215 216 ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&h->vt); 217 if (ret) { /* Version mismatch. */ 218 TRACE_KDCPOLICY_VTINIT_FAIL(context, ret); 219 free(h); 220 continue; 221 } 222 if (h->vt.init != NULL) { 223 ret = h->vt.init(context, &h->moddata); 224 if (ret == KRB5_PLUGIN_NO_HANDLE) { 225 TRACE_KDCPOLICY_INIT_SKIP(context, h->vt.name); 226 free(h); 227 continue; 228 } 229 if (ret) { 230 kdc_err(context, ret, _("while loading policy module %s"), 231 h->vt.name); 232 free(h); 233 goto cleanup; 234 } 235 } 236 handles[count++] = h; 237 } 238 239 ret = 0; 240 241 cleanup: 242 if (ret) 243 unload_kdcpolicy_plugins(context); 244 k5_plugin_free_modules(context, modules); 245 return ret; 246 } 247