xref: /freebsd/crypto/krb5/src/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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(krb5_context context,char * db_name,time_t * age)46 krb5_ldap_get_age(krb5_context context, char *db_name, time_t *age)
47 {
48     time (age);
49     return 0;
50 }
51 
52 /*
53  * read startup information - kerberos and realm container
54  */
55 krb5_error_code
krb5_ldap_read_startup_information(krb5_context context)56 krb5_ldap_read_startup_information(krb5_context context)
57 {
58     krb5_error_code      retval = 0;
59     kdb5_dal_handle      *dal_handle=NULL;
60     krb5_ldap_context    *ldap_context=NULL;
61     int                  mask = 0;
62 
63     SETUP_CONTEXT();
64     if ((retval=krb5_ldap_read_krbcontainer_dn(context, &(ldap_context->container_dn)))) {
65         k5_prependmsg(context, retval, _("Unable to read Kerberos container"));
66         goto cleanup;
67     }
68 
69     if ((retval=krb5_ldap_read_realm_params(context, context->default_realm, &(ldap_context->lrparams), &mask))) {
70         k5_prependmsg(context, retval, _("Unable to read Realm"));
71         goto cleanup;
72     }
73 
74     if (((mask & LDAP_REALM_MAXTICKETLIFE) == 0) || ((mask & LDAP_REALM_MAXRENEWLIFE) == 0)
75         || ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0)) {
76         kadm5_config_params  params_in, params_out;
77 
78         memset(&params_in, 0, sizeof(params_in));
79         memset(&params_out, 0, sizeof(params_out));
80 
81         retval = kadm5_get_config_params(context, 1, &params_in, &params_out);
82         if (retval) {
83             if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) {
84                 ldap_context->lrparams->max_life = 24 * 60 * 60; /* 1 day */
85             }
86             if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) {
87                 ldap_context->lrparams->max_renewable_life = 0;
88             }
89             if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) {
90                 ldap_context->lrparams->tktflags = KRB5_KDB_DEF_FLAGS;
91             }
92             retval = 0;
93             goto cleanup;
94         }
95 
96         if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) {
97             if (params_out.mask & KADM5_CONFIG_MAX_LIFE)
98                 ldap_context->lrparams->max_life = params_out.max_life;
99         }
100 
101         if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) {
102             if (params_out.mask & KADM5_CONFIG_MAX_RLIFE)
103                 ldap_context->lrparams->max_renewable_life = params_out.max_rlife;
104         }
105 
106         if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) {
107             if (params_out.mask & KADM5_CONFIG_FLAGS)
108                 ldap_context->lrparams->tktflags = params_out.flags;
109         }
110 
111         kadm5_free_config_params(context, &params_out);
112     }
113 
114 cleanup:
115     return retval;
116 }
117 
118 
119 /* Interrogate the root DSE (zero length DN) for an attribute value assertion.
120  * Return true if it is present, false if it is absent or we can't tell. */
121 static krb5_boolean
has_rootdse_ava(krb5_context context,const char * server_name,const char * attribute,const char * value)122 has_rootdse_ava(krb5_context context, const char *server_name,
123                 const char *attribute, const char *value)
124 {
125     krb5_boolean result = FALSE;
126     char *attrs[2], **values = NULL;
127     int i, st;
128     LDAP *ld = NULL;
129     LDAPMessage *msg, *res = NULL;
130     struct berval cred;
131 
132     attrs[0] = (char *)attribute;
133     attrs[1] = NULL;
134 
135     st = ldap_initialize(&ld, server_name);
136     if (st != LDAP_SUCCESS)
137         goto cleanup;
138 
139     /* Bind anonymously. */
140     cred.bv_val = "";
141     cred.bv_len = 0;
142     st = ldap_sasl_bind_s(ld, "", NULL, &cred, NULL, NULL, NULL);
143     if (st != LDAP_SUCCESS)
144         goto cleanup;
145 
146     st = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, NULL, attrs, 0, NULL,
147                            NULL, NULL, 0, &res);
148     if (st != LDAP_SUCCESS)
149         goto cleanup;
150 
151     msg = ldap_first_message(ld, res);
152     if (msg == NULL)
153         goto cleanup;
154 
155     values = ldap_get_values(ld, msg, attribute);
156     if (values == NULL)
157         goto cleanup;
158 
159     for (i = 0; values[i] != NULL; i++) {
160         if (strcmp(values[i], value) == 0) {
161             result = TRUE;
162             goto cleanup;
163         }
164     }
165 
166 cleanup:
167     ldap_value_free(values);
168     ldap_msgfree(res);
169     ldap_unbind_ext_s(ld, NULL, NULL);
170 
171     return result;
172 }
173 
174 krb5_boolean
has_modify_increment(krb5_context context,const char * server_name)175 has_modify_increment(krb5_context context, const char *server_name)
176 {
177     return has_rootdse_ava(context, server_name, "supportedFeatures",
178                            "1.3.6.1.1.14");
179 }
180 
181 void *
krb5_ldap_alloc(krb5_context context,void * ptr,size_t size)182 krb5_ldap_alloc(krb5_context context, void *ptr, size_t size)
183 {
184     return realloc(ptr, size);
185 }
186 
187 void
krb5_ldap_free(krb5_context context,void * ptr)188 krb5_ldap_free(krb5_context context, void *ptr)
189 {
190     free(ptr);
191 }
192 
193 krb5_error_code
krb5_ldap_open(krb5_context context,char * conf_section,char ** db_args,int mode)194 krb5_ldap_open(krb5_context context, char *conf_section, char **db_args,
195                int mode)
196 {
197     krb5_error_code status  = 0;
198     krb5_ldap_context *ldap_context=NULL;
199 
200     /* Clear the global error string */
201     krb5_clear_error_message(context);
202 
203     ldap_context = k5alloc(sizeof(krb5_ldap_context), &status);
204     if (ldap_context == NULL)
205         goto clean_n_exit;
206     context->dal_handle->db_context = ldap_context;
207     ldap_context->kcontext = context;
208 
209     status = krb5_ldap_parse_db_params(context, db_args);
210     if (status) {
211         k5_prependmsg(context, status, _("Error processing LDAP DB params"));
212         goto clean_n_exit;
213     }
214 
215     status = krb5_ldap_read_server_params(context, conf_section, mode & 0x0300);
216     if (status) {
217         k5_prependmsg(context, status, _("Error reading LDAP server params"));
218         goto clean_n_exit;
219     }
220     if ((status=krb5_ldap_db_init(context, ldap_context)) != 0) {
221         goto clean_n_exit;
222     }
223 
224     if ((status=krb5_ldap_read_startup_information(context)) != 0) {
225         goto clean_n_exit;
226     }
227 
228 clean_n_exit:
229     /* may be clearing up is not required  db_fini might do it for us, check out */
230     if (status) {
231         krb5_ldap_close(context);
232     }
233     return status;
234 }
235 
236 #include "ldap_err.h"
237 int
set_ldap_error(krb5_context ctx,int st,int op)238 set_ldap_error(krb5_context ctx, int st, int op)
239 {
240     int translated_st = translate_ldap_error(st, op);
241     k5_setmsg(ctx, translated_st, "%s", ldap_err2string(st));
242     return translated_st;
243 }
244 
245 extern krb5int_access accessor;
246 MAKE_INIT_FUNCTION(kldap_init_fn);
247 
248 int
kldap_init_fn(void)249 kldap_init_fn(void)
250 {
251     /* Global (per-module) initialization.  */
252     return krb5int_accessor (&accessor, KRB5INT_ACCESS_VERSION);
253 }
254 
255 int
kldap_ensure_initialized(void)256 kldap_ensure_initialized(void)
257 {
258     return CALL_INIT_FUNCTION (kldap_init_fn);
259 }
260 
261 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)262 krb5_ldap_check_policy_as(krb5_context kcontext, krb5_kdc_req *request,
263                           krb5_db_entry *client, krb5_db_entry *server,
264                           krb5_timestamp kdc_time, const char **status,
265                           krb5_pa_data ***e_data)
266 {
267     krb5_error_code retval;
268 
269     retval = krb5_ldap_lockout_check_policy(kcontext, client, kdc_time);
270     if (retval == KRB5KDC_ERR_CLIENT_REVOKED)
271         *status = "LOCKED_OUT";
272     return retval;
273 }
274 
275 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)276 krb5_ldap_audit_as_req(krb5_context kcontext, krb5_kdc_req *request,
277                        const krb5_address *local_addr,
278                        const krb5_address *remote_addr, krb5_db_entry *client,
279                        krb5_db_entry *server, krb5_timestamp authtime,
280                        krb5_error_code error_code)
281 {
282     (void) krb5_ldap_lockout_audit(kcontext, client, authtime, error_code);
283 }
284 
285 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)286 krb5_ldap_check_allowed_to_delegate(krb5_context context,
287                                     krb5_const_principal client,
288                                     const krb5_db_entry *server,
289                                     krb5_const_principal proxy)
290 {
291     krb5_error_code code;
292     krb5_tl_data *tlp;
293 
294     code = KRB5KDC_ERR_BADOPTION;
295 
296     for (tlp = server->tl_data; tlp != NULL; tlp = tlp->tl_data_next) {
297         krb5_principal acl;
298 
299         if (tlp->tl_data_type != KRB5_TL_CONSTRAINED_DELEGATION_ACL ||
300             tlp->tl_data_length < 1 ||
301             tlp->tl_data_contents[tlp->tl_data_length - 1] != '\0')
302             continue;
303 
304         if (krb5_parse_name(context, (char *)tlp->tl_data_contents, &acl) != 0)
305             continue;
306 
307         if (proxy == NULL || krb5_principal_compare(context, proxy, acl)) {
308             code = 0;
309             krb5_free_principal(context, acl);
310             break;
311         }
312         krb5_free_principal(context, acl);
313     }
314 
315     return code;
316 }
317