Imported Upstream version 1.17
[platform/upstream/krb5.git] / src / kdc / policy.c
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, rlife;
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, rlife;
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 }