xref: /freebsd/crypto/krb5/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c */
3 /*
4  * Copyright (c) 2004-2005, Novell, 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 are met:
9  *
10  *   * Redistributions of source code must retain the above copyright notice,
11  *       this list of conditions and the following disclaimer.
12  *   * Redistributions in binary form must reproduce the above copyright
13  *       notice, this list of conditions and the following disclaimer in the
14  *       documentation and/or other materials provided with the distribution.
15  *   * The copyright holder's name is not used to endorse or promote products
16  *       derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "autoconf.h"
32 #if HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35 
36 #include <ctype.h>
37 #include "kdb_ldap.h"
38 #include "ldap_misc.h"
39 #include <kdb5.h>
40 #include <kadm5/admin.h>
41 
42 /*
43  * ldap get age
44  */
45 krb5_error_code
krb5_ldap_get_age(context,db_name,age)46 krb5_ldap_get_age(context, db_name, age)
47     krb5_context context;
48     char *db_name;
49     time_t *age;
50 {
51     time (age);
52     return 0;
53 }
54 
55 /*
56  * read startup information - kerberos and realm container
57  */
58 krb5_error_code
krb5_ldap_read_startup_information(krb5_context context)59 krb5_ldap_read_startup_information(krb5_context context)
60 {
61     krb5_error_code      retval = 0;
62     kdb5_dal_handle      *dal_handle=NULL;
63     krb5_ldap_context    *ldap_context=NULL;
64     int                  mask = 0;
65 
66     SETUP_CONTEXT();
67     if ((retval=krb5_ldap_read_krbcontainer_dn(context, &(ldap_context->container_dn)))) {
68         k5_prependmsg(context, retval, _("Unable to read Kerberos container"));
69         goto cleanup;
70     }
71 
72     if ((retval=krb5_ldap_read_realm_params(context, context->default_realm, &(ldap_context->lrparams), &mask))) {
73         k5_prependmsg(context, retval, _("Unable to read Realm"));
74         goto cleanup;
75     }
76 
77     if (((mask & LDAP_REALM_MAXTICKETLIFE) == 0) || ((mask & LDAP_REALM_MAXRENEWLIFE) == 0)
78         || ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0)) {
79         kadm5_config_params  params_in, params_out;
80 
81         memset(&params_in, 0, sizeof(params_in));
82         memset(&params_out, 0, sizeof(params_out));
83 
84         retval = kadm5_get_config_params(context, 1, &params_in, &params_out);
85         if (retval) {
86             if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) {
87                 ldap_context->lrparams->max_life = 24 * 60 * 60; /* 1 day */
88             }
89             if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) {
90                 ldap_context->lrparams->max_renewable_life = 0;
91             }
92             if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) {
93                 ldap_context->lrparams->tktflags = KRB5_KDB_DEF_FLAGS;
94             }
95             retval = 0;
96             goto cleanup;
97         }
98 
99         if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) {
100             if (params_out.mask & KADM5_CONFIG_MAX_LIFE)
101                 ldap_context->lrparams->max_life = params_out.max_life;
102         }
103 
104         if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) {
105             if (params_out.mask & KADM5_CONFIG_MAX_RLIFE)
106                 ldap_context->lrparams->max_renewable_life = params_out.max_rlife;
107         }
108 
109         if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) {
110             if (params_out.mask & KADM5_CONFIG_FLAGS)
111                 ldap_context->lrparams->tktflags = params_out.flags;
112         }
113 
114         kadm5_free_config_params(context, &params_out);
115     }
116 
117 cleanup:
118     return retval;
119 }
120 
121 
122 /* Interrogate the root DSE (zero length DN) for an attribute value assertion.
123  * Return true if it is present, false if it is absent or we can't tell. */
124 static krb5_boolean
has_rootdse_ava(krb5_context context,const char * server_name,const char * attribute,const char * value)125 has_rootdse_ava(krb5_context context, const char *server_name,
126                 const char *attribute, const char *value)
127 {
128     krb5_boolean result = FALSE;
129     char *attrs[2], **values = NULL;
130     int i, st;
131     LDAP *ld = NULL;
132     LDAPMessage *msg, *res = NULL;
133     struct berval cred;
134 
135     attrs[0] = (char *)attribute;
136     attrs[1] = NULL;
137 
138     st = ldap_initialize(&ld, server_name);
139     if (st != LDAP_SUCCESS)
140         goto cleanup;
141 
142     /* Bind anonymously. */
143     cred.bv_val = "";
144     cred.bv_len = 0;
145     st = ldap_sasl_bind_s(ld, "", NULL, &cred, NULL, NULL, NULL);
146     if (st != LDAP_SUCCESS)
147         goto cleanup;
148 
149     st = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, NULL, attrs, 0, NULL,
150                            NULL, NULL, 0, &res);
151     if (st != LDAP_SUCCESS)
152         goto cleanup;
153 
154     msg = ldap_first_message(ld, res);
155     if (msg == NULL)
156         goto cleanup;
157 
158     values = ldap_get_values(ld, msg, attribute);
159     if (values == NULL)
160         goto cleanup;
161 
162     for (i = 0; values[i] != NULL; i++) {
163         if (strcmp(values[i], value) == 0) {
164             result = TRUE;
165             goto cleanup;
166         }
167     }
168 
169 cleanup:
170     ldap_value_free(values);
171     ldap_msgfree(res);
172     ldap_unbind_ext_s(ld, NULL, NULL);
173 
174     return result;
175 }
176 
177 krb5_boolean
has_modify_increment(krb5_context context,const char * server_name)178 has_modify_increment(krb5_context context, const char *server_name)
179 {
180     return has_rootdse_ava(context, server_name, "supportedFeatures",
181                            "1.3.6.1.1.14");
182 }
183 
184 void *
krb5_ldap_alloc(krb5_context context,void * ptr,size_t size)185 krb5_ldap_alloc(krb5_context context, void *ptr, size_t size)
186 {
187     return realloc(ptr, size);
188 }
189 
190 void
krb5_ldap_free(krb5_context context,void * ptr)191 krb5_ldap_free(krb5_context context, void *ptr)
192 {
193     free(ptr);
194 }
195 
196 krb5_error_code
krb5_ldap_open(krb5_context context,char * conf_section,char ** db_args,int mode)197 krb5_ldap_open(krb5_context context, char *conf_section, char **db_args,
198                int mode)
199 {
200     krb5_error_code status  = 0;
201     krb5_ldap_context *ldap_context=NULL;
202 
203     /* Clear the global error string */
204     krb5_clear_error_message(context);
205 
206     ldap_context = k5alloc(sizeof(krb5_ldap_context), &status);
207     if (ldap_context == NULL)
208         goto clean_n_exit;
209     context->dal_handle->db_context = ldap_context;
210     ldap_context->kcontext = context;
211 
212     status = krb5_ldap_parse_db_params(context, db_args);
213     if (status) {
214         k5_prependmsg(context, status, _("Error processing LDAP DB params"));
215         goto clean_n_exit;
216     }
217 
218     status = krb5_ldap_read_server_params(context, conf_section, mode & 0x0300);
219     if (status) {
220         k5_prependmsg(context, status, _("Error reading LDAP server params"));
221         goto clean_n_exit;
222     }
223     if ((status=krb5_ldap_db_init(context, ldap_context)) != 0) {
224         goto clean_n_exit;
225     }
226 
227     if ((status=krb5_ldap_read_startup_information(context)) != 0) {
228         goto clean_n_exit;
229     }
230 
231 clean_n_exit:
232     /* may be clearing up is not required  db_fini might do it for us, check out */
233     if (status) {
234         krb5_ldap_close(context);
235     }
236     return status;
237 }
238 
239 #include "ldap_err.h"
240 int
set_ldap_error(krb5_context ctx,int st,int op)241 set_ldap_error(krb5_context ctx, int st, int op)
242 {
243     int translated_st = translate_ldap_error(st, op);
244     k5_setmsg(ctx, translated_st, "%s", ldap_err2string(st));
245     return translated_st;
246 }
247 
248 extern krb5int_access accessor;
249 MAKE_INIT_FUNCTION(kldap_init_fn);
250 
251 int
kldap_init_fn(void)252 kldap_init_fn(void)
253 {
254     /* Global (per-module) initialization.  */
255     return krb5int_accessor (&accessor, KRB5INT_ACCESS_VERSION);
256 }
257 
258 int
kldap_ensure_initialized(void)259 kldap_ensure_initialized(void)
260 {
261     return CALL_INIT_FUNCTION (kldap_init_fn);
262 }
263 
264 krb5_error_code
krb5_ldap_check_policy_as(krb5_context kcontext,krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,krb5_timestamp kdc_time,const char ** status,krb5_pa_data *** e_data)265 krb5_ldap_check_policy_as(krb5_context kcontext, krb5_kdc_req *request,
266                           krb5_db_entry *client, krb5_db_entry *server,
267                           krb5_timestamp kdc_time, const char **status,
268                           krb5_pa_data ***e_data)
269 {
270     krb5_error_code retval;
271 
272     retval = krb5_ldap_lockout_check_policy(kcontext, client, kdc_time);
273     if (retval == KRB5KDC_ERR_CLIENT_REVOKED)
274         *status = "LOCKED_OUT";
275     return retval;
276 }
277 
278 void
krb5_ldap_audit_as_req(krb5_context kcontext,krb5_kdc_req * request,const krb5_address * local_addr,const krb5_address * remote_addr,krb5_db_entry * client,krb5_db_entry * server,krb5_timestamp authtime,krb5_error_code error_code)279 krb5_ldap_audit_as_req(krb5_context kcontext, krb5_kdc_req *request,
280                        const krb5_address *local_addr,
281                        const krb5_address *remote_addr, krb5_db_entry *client,
282                        krb5_db_entry *server, krb5_timestamp authtime,
283                        krb5_error_code error_code)
284 {
285     (void) krb5_ldap_lockout_audit(kcontext, client, authtime, error_code);
286 }
287 
288 krb5_error_code
krb5_ldap_check_allowed_to_delegate(krb5_context context,krb5_const_principal client,const krb5_db_entry * server,krb5_const_principal proxy)289 krb5_ldap_check_allowed_to_delegate(krb5_context context,
290                                     krb5_const_principal client,
291                                     const krb5_db_entry *server,
292                                     krb5_const_principal proxy)
293 {
294     krb5_error_code code;
295     krb5_tl_data *tlp;
296 
297     code = KRB5KDC_ERR_BADOPTION;
298 
299     for (tlp = server->tl_data; tlp != NULL; tlp = tlp->tl_data_next) {
300         krb5_principal acl;
301 
302         if (tlp->tl_data_type != KRB5_TL_CONSTRAINED_DELEGATION_ACL)
303             continue;
304 
305         if (krb5_parse_name(context, (char *)tlp->tl_data_contents, &acl) != 0)
306             continue;
307 
308         if (proxy == NULL || krb5_principal_compare(context, proxy, acl)) {
309             code = 0;
310             krb5_free_principal(context, acl);
311             break;
312         }
313         krb5_free_principal(context, acl);
314     }
315 
316     return code;
317 }
318