xref: /titanic_44/usr/src/lib/krb5/plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c (revision 159d09a20817016f09b3ea28d1bdada4a336bb91)
1  /*
2   * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3   * Use is subject to license terms.
4   */
5  
6  
7  /*
8   * lib/kdb/kdb_ldap/kdb_ldap.c
9   *
10   * Copyright (c) 2004-2005, Novell, Inc.
11   * All rights reserved.
12   *
13   * Redistribution and use in source and binary forms, with or without
14   * modification, are permitted provided that the following conditions are met:
15   *
16   *   * Redistributions of source code must retain the above copyright notice,
17   *       this list of conditions and the following disclaimer.
18   *   * Redistributions in binary form must reproduce the above copyright
19   *       notice, this list of conditions and the following disclaimer in the
20   *       documentation and/or other materials provided with the distribution.
21   *   * The copyright holder's name is not used to endorse or promote products
22   *       derived from this software without specific prior written permission.
23   *
24   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34   * POSSIBILITY OF SUCH DAMAGE.
35   */
36  
37  #include "autoconf.h"
38  #if HAVE_UNISTD_H
39  #include <unistd.h>
40  #endif
41  
42  #include <ctype.h>
43  #include "kdb_ldap.h"
44  #include "ldap_misc.h"
45  #include "ldap_main.h"
46  #include <kdb5.h>
47  #include <kadm5/admin.h>
48  /* Solaris Kerberos: needed for MAKE_INIT_FUNCTION() */
49  #include <k5-platform.h>
50  #include <k5-int.h>
51  #include <libintl.h>
52  
53  krb5_error_code
krb5_ldap_get_db_opt(char * input,char ** opt,char ** val)54  krb5_ldap_get_db_opt(char *input, char **opt, char **val)
55  {
56      char *pos = strchr(input, '=');
57  
58      *val = NULL;
59      if (pos == NULL) {
60  	*opt = strdup(input);
61  	if (*opt == NULL) {
62  	    return ENOMEM;
63  	}
64      } else {
65  	int len = pos - input;
66  	*opt = malloc((unsigned) len + 1);
67  	if (!*opt) {
68  	    return ENOMEM;
69  	}
70  	memcpy(*opt, input, (unsigned) len);
71  	/* ignore trailing blanks */
72  	while (isblank((*opt)[len-1]))
73  	    --len;
74  	(*opt)[len] = '\0';
75  
76  	pos += 1; /* move past '=' */
77  	while (isblank(*pos))  /* ignore leading blanks */
78  	    pos += 1;
79  	if (*pos != '\0') {
80  	    *val = strdup (pos);
81  	    if (!*val) {
82  		free (*opt);
83  		return ENOMEM;
84  	    }
85  	}
86      }
87      return (0);
88  
89  }
90  
91  
92  /*
93   * ldap get age
94   */
95  /*ARGSUSED*/
96  krb5_error_code
krb5_ldap_db_get_age(context,db_name,age)97  krb5_ldap_db_get_age(context, db_name, age)
98      krb5_context context;
99      char *db_name;
100      time_t *age;
101  {
102      time (age);
103      return 0;
104  }
105  
106  /*
107   * read startup information - kerberos and realm container
108   */
109  krb5_error_code
krb5_ldap_read_startup_information(krb5_context context)110  krb5_ldap_read_startup_information(krb5_context context)
111  {
112      krb5_error_code      retval = 0;
113      kdb5_dal_handle      *dal_handle=NULL;
114      krb5_ldap_context    *ldap_context=NULL;
115      int                  mask = 0;
116  
117      SETUP_CONTEXT();
118      if ((retval=krb5_ldap_read_krbcontainer_params(context, &(ldap_context->krbcontainer)))) {
119  	prepend_err_str (context, gettext("Unable to read Kerberos container"), retval, retval);
120  	goto cleanup;
121      }
122  
123      if ((retval=krb5_ldap_read_realm_params(context, context->default_realm, &(ldap_context->lrparams), &mask))) {
124  	prepend_err_str (context, gettext("Unable to read Realm"), retval, retval);
125  	goto cleanup;
126      }
127  
128      if (((mask & LDAP_REALM_MAXTICKETLIFE) == 0) || ((mask & LDAP_REALM_MAXRENEWLIFE) == 0)
129                                                   || ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0)) {
130          kadm5_config_params  params_in, params_out;
131  
132          memset((char *) &params_in, 0, sizeof(params_in));
133          memset((char *) &params_out, 0, sizeof(params_out));
134  
135          retval = kadm5_get_config_params(context, 1, &params_in, &params_out);
136          if (retval) {
137              if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) {
138                  ldap_context->lrparams->max_life = 24 * 60 * 60; /* 1 day */
139              }
140              if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) {
141                  ldap_context->lrparams->max_renewable_life = 0;
142              }
143              if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) {
144                  ldap_context->lrparams->tktflags = KRB5_KDB_DEF_FLAGS;
145              }
146              retval = 0;
147              goto cleanup;
148          }
149  
150          if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) {
151              if (params_out.mask & KADM5_CONFIG_MAX_LIFE)
152                  ldap_context->lrparams->max_life = params_out.max_life;
153          }
154  
155          if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) {
156              if (params_out.mask & KADM5_CONFIG_MAX_RLIFE)
157                  ldap_context->lrparams->max_renewable_life = params_out.max_rlife;
158          }
159  
160          if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) {
161              if (params_out.mask & KADM5_CONFIG_FLAGS)
162                  ldap_context->lrparams->tktflags = params_out.flags;
163          }
164  
165          kadm5_free_config_params(context, &params_out);
166      }
167  
168  cleanup:
169      return retval;
170  }
171  
172  
173  /* Function to check if a LDAP server supports the SASL external mechanism
174   *Return values:
175   *   0 => supports
176   *   1 => does not support
177   *   2 => don't know
178   */
179  #define ERR_MSG1 "Unable to check if SASL EXTERNAL mechanism is supported by LDAP server. Proceeding anyway ..."
180  #define ERR_MSG2 "SASL EXTERNAL mechanism not supported by LDAP server. Can't perform certificate-based bind."
181  
182  int
has_sasl_external_mech(context,ldap_server)183  has_sasl_external_mech(context, ldap_server)
184      krb5_context     context;
185      char             *ldap_server;
186  {
187      int               i=0, flag=0, ret=0, retval=0;
188      char              *attrs[]={"supportedSASLMechanisms", NULL}, **values=NULL;
189      LDAP              *ld=NULL;
190      LDAPMessage       *msg=NULL, *res=NULL;
191  
192      /*
193       * Solaris Kerberos: don't use SSL since we are checking to see if SASL
194       * Externnal mech is supported.
195       */
196      retval = ldap_initialize(&ld, ldap_server, SSL_OFF, NULL);
197      if (retval != LDAP_SUCCESS) {
198  	krb5_set_error_message(context, 2, "%s", ERR_MSG1);
199  	ret = 2; /* Don't know */
200  	goto cleanup;
201      }
202  
203      /* Solaris Kerberos: anon bind not needed */
204  #if 0 /************** Begin IFDEF'ed OUT *******************************/
205      /* Anonymous bind */
206      retval = ldap_sasl_bind_s(ld, NULL, NULL, NULL, NULL, NULL, NULL);
207      if (retval != LDAP_SUCCESS) {
208  	krb5_set_error_message(context, 2, "%s", ERR_MSG1);
209  	ret = 2; /* Don't know */
210  	goto cleanup;
211      }
212  #endif /**************** END IFDEF'ed OUT *******************************/
213  
214      retval = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, NULL, attrs, 0, NULL, NULL, NULL, 0, &res);
215      if (retval != LDAP_SUCCESS) {
216  	krb5_set_error_message(context, 2, "%s", ERR_MSG1);
217  	ret = 2; /* Don't know */
218  	goto cleanup;
219      }
220  
221  #if 0 /************** Begin IFDEF'ed OUT *******************************/
222      msg = ldap_first_message(ld, res);
223  #else
224      /* Solaris Kerberos: more accurate */
225      msg = ldap_first_entry(ld, res);
226  #endif /**************** END IFDEF'ed OUT *******************************/
227      if (msg == NULL) {
228  	krb5_set_error_message(context, 2, "%s", ERR_MSG1);
229  	ret = 2; /* Don't know */
230  	goto cleanup;
231      }
232  
233      values = ldap_get_values(ld, msg, "supportedSASLMechanisms");
234      if (values == NULL) {
235  	krb5_set_error_message(context, 1, "%s", ERR_MSG2);
236  	ret = 1; /* Not supported */
237  	goto cleanup;
238      }
239  
240      for (i = 0; values[i] != NULL; i++) {
241  	if (strcmp(values[i], "EXTERNAL"))
242  	    continue;
243  	flag = 1;
244      }
245  
246      if (flag != 1) {
247  	krb5_set_error_message(context, 1, "%s", ERR_MSG2);
248  	ret = 1; /* Not supported */
249  	goto cleanup;
250      }
251  
252  cleanup:
253  
254      if (values != NULL)
255  	ldap_value_free(values);
256  
257      if (res != NULL)
258  	ldap_msgfree(res);
259  
260      if (ld != NULL)
261  	ldap_unbind_ext_s(ld, NULL, NULL);
262  
263      return ret;
264  }
265  
266  /*ARGSUSED*/
krb5_ldap_alloc(krb5_context context,void * ptr,size_t size)267  void * krb5_ldap_alloc(krb5_context context, void *ptr, size_t size)
268  {
269      return realloc(ptr, size);
270  }
271  
272  /*ARGSUSED*/
krb5_ldap_free(krb5_context context,void * ptr)273  void krb5_ldap_free(krb5_context context, void *ptr)
274  
275  {
276      free(ptr);
277  }
278  
krb5_ldap_open(krb5_context context,char * conf_section,char ** db_args,int mode)279  krb5_error_code krb5_ldap_open(krb5_context context,
280  			       char *conf_section,
281  			       char **db_args,
282  			       int mode)
283  {
284      krb5_error_code status  = 0;
285      char **t_ptr = db_args;
286      krb5_ldap_context *ldap_context=NULL;
287      int srv_cnt = 0;
288      kdb5_dal_handle *dal_handle=NULL;
289  
290      /* Clear the global error string */
291      krb5_clear_error_message(context);
292  
293      ldap_context = calloc(1, sizeof(krb5_ldap_context));
294      if (ldap_context == NULL) {
295  	status = ENOMEM;
296  	goto clean_n_exit;
297      }
298  
299      ldap_context->kcontext = context;
300  
301      while (t_ptr && *t_ptr) {
302  	char *opt = NULL, *val = NULL;
303  
304  	if ((status = krb5_ldap_get_db_opt(*t_ptr, &opt, &val)) != 0) {
305  	    goto clean_n_exit;
306  	}
307  	if (opt && !strcmp(opt, "binddn")) {
308  	    if (ldap_context->bind_dn) {
309  		free (opt);
310  		free (val);
311  		status = EINVAL;
312  		krb5_set_error_message (context, status, gettext("'binddn' missing"));
313  		goto clean_n_exit;
314  	    }
315  	    if (val == NULL) {
316  		status = EINVAL;
317  		krb5_set_error_message (context, status, gettext("'binddn' value missing"));
318  		free(opt);
319  		goto clean_n_exit;
320  	    }
321  	    ldap_context->bind_dn = strdup(val);
322  	    if (ldap_context->bind_dn == NULL) {
323  		free (opt);
324  		free (val);
325  		status = ENOMEM;
326  		goto clean_n_exit;
327  	    }
328  	} else if (opt && !strcmp(opt, "nconns")) {
329  	    if (ldap_context->max_server_conns) {
330  		free (opt);
331  		free (val);
332  		status = EINVAL;
333  		krb5_set_error_message (context, status, gettext("'nconns' missing"));
334  		goto clean_n_exit;
335  	    }
336  	    if (val == NULL) {
337  		status = EINVAL;
338  		krb5_set_error_message (context, status, gettext("'nconns' value missing"));
339  		free(opt);
340  		goto clean_n_exit;
341  	    }
342  	    ldap_context->max_server_conns = atoi(val) ? atoi(val) : DEFAULT_CONNS_PER_SERVER;
343  	} else if (opt && !strcmp(opt, "bindpwd")) {
344  	    if (ldap_context->bind_pwd) {
345  		free (opt);
346  		free (val);
347  		status = EINVAL;
348  		krb5_set_error_message (context, status, gettext("'bindpwd' missing"));
349  		goto clean_n_exit;
350  	    }
351  	    if (val == NULL) {
352  		status = EINVAL;
353  		krb5_set_error_message (context, status, gettext("'bindpwd' value missing"));
354  		free(opt);
355  		goto clean_n_exit;
356  	    }
357  	    ldap_context->bind_pwd = strdup(val);
358  	    if (ldap_context->bind_pwd == NULL) {
359  		free (opt);
360  		free (val);
361  		status = ENOMEM;
362  		goto clean_n_exit;
363  	    }
364  	} else if (opt && !strcmp(opt, "host")) {
365  	    if (val == NULL) {
366  		status = EINVAL;
367  		krb5_set_error_message (context, status, gettext("'host' value missing"));
368  		free(opt);
369  		goto clean_n_exit;
370  	    }
371  	    if (ldap_context->server_info_list == NULL)
372  		ldap_context->server_info_list = (krb5_ldap_server_info **) calloc (SERV_COUNT+1, sizeof (krb5_ldap_server_info *)) ;
373  
374  	    if (ldap_context->server_info_list == NULL) {
375  		free (opt);
376  		free (val);
377  		status = ENOMEM;
378  		goto clean_n_exit;
379  	    }
380  
381  	    ldap_context->server_info_list[srv_cnt] = (krb5_ldap_server_info *) calloc (1, sizeof (krb5_ldap_server_info));
382  	    if (ldap_context->server_info_list[srv_cnt] == NULL) {
383  		free (opt);
384  		free (val);
385  		status = ENOMEM;
386  		goto clean_n_exit;
387  	    }
388  
389  	    ldap_context->server_info_list[srv_cnt]->server_status = NOTSET;
390  
391  	    ldap_context->server_info_list[srv_cnt]->server_name = strdup(val);
392  	    if (ldap_context->server_info_list[srv_cnt]->server_name == NULL) {
393  		free (opt);
394  		free (val);
395  		status = ENOMEM;
396  		goto clean_n_exit;
397  	    }
398  
399  	    srv_cnt++;
400  #ifdef HAVE_EDIRECTORY
401  	} else if (opt && !strcmp(opt, "cert")) {
402  	    if (val == NULL) {
403  		status = EINVAL;
404  		krb5_set_error_message (context, status, gettext("'cert' value missing"));
405  		free(opt);
406  		goto clean_n_exit;
407  	    }
408  
409  	    if (ldap_context->root_certificate_file == NULL) {
410  		ldap_context->root_certificate_file = strdup(val);
411  		if (ldap_context->root_certificate_file == NULL) {
412  		    free (opt);
413  		    free (val);
414  		    status = ENOMEM;
415  		    goto clean_n_exit;
416  		}
417  	    } else {
418  		void *tmp=NULL;
419  		char *oldstr = NULL;
420  		unsigned int len=0;
421  
422  		oldstr = strdup(ldap_context->root_certificate_file);
423  		if (oldstr == NULL) {
424  		    free (opt);
425  		    free (val);
426  		    status = ENOMEM;
427  		    goto clean_n_exit;
428  		}
429  
430  		tmp = ldap_context->root_certificate_file;
431  		len = strlen(ldap_context->root_certificate_file) + 2 + strlen(val);
432  		ldap_context->root_certificate_file = realloc(ldap_context->root_certificate_file,
433  							      len);
434  		if (ldap_context->root_certificate_file == NULL) {
435  		    free (tmp);
436  		    free (opt);
437  		    free (val);
438  		    status = ENOMEM;
439  		    goto clean_n_exit;
440  		}
441  		memset(ldap_context->root_certificate_file, 0, len);
442  		sprintf(ldap_context->root_certificate_file,"%s %s", oldstr, val);
443  		free (oldstr);
444  	    }
445  #endif
446  	} else {
447  	    /* ignore hash argument. Might have been passed from create */
448  	    status = EINVAL;
449  	    if (opt && !strcmp(opt, "temporary")) {
450  		/*
451  		 * temporary is passed in when kdb5_util load without -update is done.
452  		 * This is unsupported by the LDAP plugin.
453  		 */
454  		krb5_set_error_message (context, status,
455  					gettext("open of LDAP directory aborted, plugin requires -update argument"));
456  	    } else {
457  		krb5_set_error_message (context, status, gettext("unknown option \'%s\'"),
458  					opt?opt:val);
459  	    }
460  	    free(opt);
461  	    free(val);
462  	    goto clean_n_exit;
463  	}
464  
465  	free(opt);
466  	free(val);
467  	t_ptr++;
468      }
469  
470      dal_handle = (kdb5_dal_handle *) context->db_context;
471      dal_handle->db_context = ldap_context;
472      status = krb5_ldap_read_server_params(context, conf_section, mode & 0x0300);
473      if (status) {
474  	if (ldap_context)
475  	    krb5_ldap_free_ldap_context(ldap_context);
476  	ldap_context = NULL;
477  	dal_handle->db_context = NULL;
478  	prepend_err_str (context, gettext("Error reading LDAP server params: "), status, status);
479  	goto clean_n_exit;
480      }
481      if ((status=krb5_ldap_db_init(context, ldap_context)) != 0) {
482  	goto clean_n_exit;
483      }
484  
485      if ((status=krb5_ldap_read_startup_information(context)) != 0) {
486  	goto clean_n_exit;
487      }
488  
489  clean_n_exit:
490      /* may be clearing up is not required  db_fini might do it for us, check out */
491      if (status) {
492  	krb5_ldap_close(context);
493      }
494      return status;
495  }
496  
497  #include "ldap_err.h"
498  int
set_ldap_error(krb5_context ctx,int st,int op)499  set_ldap_error (krb5_context ctx, int st, int op)
500  {
501      int translated_st = translate_ldap_error(st, op);
502      krb5_set_error_message(ctx, translated_st, "%s", ldap_err2string(st));
503      return translated_st;
504  }
505  
506  void
prepend_err_str(krb5_context ctx,const char * str,krb5_error_code err,krb5_error_code oerr)507  prepend_err_str (krb5_context ctx, const char *str, krb5_error_code err,
508  		 krb5_error_code oerr)
509  {
510      const char *omsg;
511      if (oerr == 0) oerr = err;
512      omsg = krb5_get_error_message (ctx, err);
513      krb5_set_error_message (ctx, err, "%s %s", str, omsg);
514      /* Solaris Kerberos: Memleak */
515      krb5_free_error_message(ctx, omsg);
516  }
517  
518  extern krb5int_access accessor;
519  MAKE_INIT_FUNCTION(kldap_init_fn);
520  
kldap_init_fn(void)521  int kldap_init_fn(void)
522  {
523      /* Global (per-module) initialization.  */
524      return krb5int_accessor (&accessor, KRB5INT_ACCESS_VERSION);
525  }
526  
kldap_ensure_initialized(void)527  int kldap_ensure_initialized(void)
528  {
529      return CALL_INIT_FUNCTION (kldap_init_fn);
530  }
531