1 /*
2  * Copyright (C) 1998-2008 Luke Howard.
3  * This file is part of the pam_ldap library.
4  * Contributed by Luke Howard, <lukeh@padl.com>, 1998.
5  *
6  * The pam_ldap library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * The pam_ldap library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with the pam_ldap library; see the file COPYING.LIB.  If not,
18  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 /*
23  * Portions Copyright Andrew Morgan, 1996.  All rights reserved.
24  * Modified by Alexander O. Yuriev
25  *
26  * Redistribution and use in source and binary forms, with or without
27  * modification, are permitted provided that the following conditions
28  * are met:
29  * 1. Redistributions of source code must retain the above copyright
30  *    notice, and the entire permission notice in its entirety,
31  *    including the disclaimer of warranties.
32  * 2. Redistributions in binary form must reproduce the above copyright
33  *    notice, this list of conditions and the following disclaimer in the
34  *    documentation and/or other materials provided with the distribution.
35  * 3. The name of the author may not be used to endorse or promote
36  *    products derived from this software without specific prior
37  *    written permission.
38  *
39  * ALTERNATIVELY, this product may be distributed under the terms of
40  * the GNU Public License, in which case the provisions of the GPL are
41  * required INSTEAD OF the above restrictions.  (This clause is
42  * necessary due to a potential bad interaction between the GPL and
43  * the restrictions contained in a BSD-style copyright.)
44  *
45  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
46  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
47  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
48  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
49  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
50  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
51  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
53  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
54  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
55  * OF THE POSSIBILITY OF SUCH DAMAGE.
56  *
57  * Portions by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
58  * Copyright (C) 1996.
59  *
60  * Redistribution and use in source and binary forms, with or without
61  * modification, are permitted provided that the following conditions
62  * are met:
63  * 1. Redistributions of source code must retain the above copyright
64  *    notice, and the entire permission notice in its entirety,
65  *    including the disclaimer of warranties.
66  * 2. Redistributions in binary form must reproduce the above copyright
67  *    notice, this list of conditions and the following disclaimer in the
68  *    documentation and/or other materials provided with the distribution.
69  * 3. The name of the author may not be used to endorse or promote
70  *    products derived from this software without specific prior
71  *    written permission.
72  *
73  * ALTERNATIVELY, this product may be distributed under the terms of
74  * the GNU Public License, in which case the provisions of the GPL are
75  * required INSTEAD OF the above restrictions.  (This clause is
76  * necessary due to a potential bad interaction between the GPL and
77  * the restrictions contained in a BSD-style copyright.)
78  *
79  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
80  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
81  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
82  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
83  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
84  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
85  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
86  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
87  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
88  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
89  * OF THE POSSIBILITY OF SUCH DAMAGE.
90  */
91 
92 #include "config.h"
93 
94 #include <stdio.h>
95 #include <stdlib.h>
96 #include <string.h>
97 #include <time.h>
98 #include <sys/time.h>
99 #include <sys/param.h>
100 #include <unistd.h>
101 #include <syslog.h>
102 #include <netdb.h>
103 #include <errno.h>
104 
105 #if defined(HAVE_CRYPT_H)
106 #include <crypt.h>
107 #elif defined(HAVE_DES_H)
108 #include <des.h>
109 #endif
110 
111 #ifdef HAVE_LBER_H
112 #include <lber.h>
113 #endif
114 #ifdef HAVE_LDAP_H
115 #include <ldap.h>
116 #endif
117 #ifdef HAVE_LDAP_SSL_H
118 #include <ldap_ssl.h>
119 #endif
120 #ifdef HAVE_SASL_SASL_H
121 #include <sasl/sasl.h>
122 #elif defined(HAVE_SASL_H)
123 #include <sasl.h>
124 #endif
125 
126 #ifdef YPLDAPD
127 #include <rpcsvc/yp_prot.h>
128 #include <rpcsvc/ypclnt.h>
129 #endif /* YPLDAPD */
130 
131 #include "pam_ldap.h"
132 #include "md5.h"
133 
134 #define CONST_ARG const
135 
136 #ifndef HAVE_LDAP_MEMFREE
137 #define ldap_memfree(x)	free(x)
138 #endif
139 
140 #ifdef __GNUC__
141 #define __UNUSED__ __attribute__ ((unused))
142 #else
143 #define __UNUSED__
144 #endif
145 
146 static char rcsid[] __UNUSED__ =
147   "$Id: pam_ldap.c,v 1.215 2010/11/08 00:58:36 lukeh Exp $";
148 #if LDAP_SET_REBIND_PROC_ARGS < 3
149 static pam_ldap_session_t *global_session = 0;
150 #endif
151 static int pam_debug_level __UNUSED__ = 0;
152 
153 #ifdef HAVE_LDAPSSL_INIT
154 static int ssl_initialized = 0;
155 #endif
156 
157 #ifdef LBER_OPT_LOG_PRINT_FILE
158 static FILE *debugfile = NULL;
159 #endif
160 
161 static const char *policy_error_table[] = {
162   "Password Expired",
163   "Account Locked",
164   "Change After Reset",
165   "Password Modification Not Allowed",
166   "Must Supply Old Password",
167   "Insufficient Password Quality",
168   "Password Too Short",
169   "Password Too Young",
170   "Password Insufficient"
171 };
172 
173 #ifdef __GNUC__
174 #define DEBUG_MSG(level, fmt, args...)		\
175 	do {					\
176 		if (level >= pam_debug_level)	\
177 			syslog(LOG_DEBUG, "%s:%i " fmt , __FUNCTION__ , __LINE__ , ## args); \
178 	} while (0)
179 #else
180 #define DEBUG_MSG(level, fmt, ...)            \
181       do {                                    \
182               if (level >= pam_debug_level)   \
183                       syslog(LOG_DEBUG, "%s:%i " fmt , __FUNCTION__ , __LINE__ , __VA_ARGS__); \
184       } while (0)
185 #endif /* __GNUC__ */
186 
187 static int i64c (int i);
188 
189 #ifndef HAVE_LDAP_GET_LDERRNO
190 static int ldap_get_lderrno (LDAP * ld, char **m, char **s);
191 #endif
192 #ifndef HAVE_LDAP_SET_LDERRNO
193 static int ldap_set_lderrno (LDAP * ld, int e, const char *m, const char *s);
194 #endif
195 
196 static void _release_config (pam_ldap_config_t ** pconfig);
197 static void _release_user_info (pam_ldap_user_info_t ** info);
198 static void _pam_ldap_cleanup_session (pam_handle_t * pamh, void *data,
199 				       int error_status);
200 static void _cleanup_data (pam_handle_t * pamh, void *data, int error_status);
201 static void _cleanup_authtok_data (pam_handle_t * pamh, void *data,
202 				   int error_status);
203 static int _alloc_config (pam_ldap_config_t ** presult);
204 static int _get_password_policy_response_value (struct berval *response_value,
205 						pam_ldap_session_t * session);
206 #ifdef YPLDAPD
207 static int _ypldapd_read_config (pam_ldap_config_t ** presult);
208 #endif
209 static int _read_config (const char *configFile,
210 			 pam_ldap_config_t ** presult);
211 static int _open_session (pam_ldap_session_t * session);
212 
213 /* TLS routines */
214 #if defined HAVE_LDAP_START_TLS_S || (defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS))
215 static int _set_ssl_default_options (pam_ldap_session_t *);
216 static int _set_ssl_options (pam_ldap_session_t *);
217 #endif
218 
219 static int _connect_anonymously (pam_ldap_session_t * session);
220 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
221 #if LDAP_SET_REBIND_PROC_ARGS == 3
222 static int _rebind_proc (LDAP * ld, LDAP_CONST char *url, ber_tag_t request,
223 			 ber_int_t msgid, void *arg);
224 #else
225 static int _rebind_proc (LDAP * ld, LDAP_CONST char *url, int request,
226 			 ber_int_t msgid);
227 #endif
228 #else
229 #if LDAP_SET_REBIND_PROC_ARGS == 3
230 static int _rebind_proc (LDAP * ld,
231 			 char **whop, char **credp, int *methodp, int freeit,
232 			 void *arg);
233 #else
234 static int _rebind_proc (LDAP * ld, char **whop, char **credp, int *methodp,
235 			 int freeit);
236 #endif
237 #endif
238 
239 #if (defined(HAVE_SASL_SASL_H) || defined(HAVE_SASL_H)) && defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S)
240 static int _do_sasl_interaction (pam_handle_t *handle, pam_ldap_session_t *session, unsigned flags, sasl_interact_t *interact);
241 static int _do_sasl_interact (LDAP *ld, unsigned flags, void *defaults, void *interact);
242 #endif
243 
244 static int _connect_as_user (pam_handle_t *handle,
245 			     pam_ldap_session_t * session,
246 			     const char *password);
247 static int _get_integer_value (LDAP * ld, LDAPMessage * e, const char *attr,
248 			       int *ptr);
249 static int _get_long_integer_value (LDAP * ld, LDAPMessage * e,
250 				    const char *attr, long int *ptr);
251 static int _get_string_value (LDAP * ld, LDAPMessage * e, const char *attr,
252 			      char **ptr);
253 static int _get_string_values (LDAP * ld, LDAPMessage * e, const char *attr,
254 			       char ***ptr);
255 static int _has_deny_value (char **src, const char *tgt);
256 static int _has_value (char **src, const char *tgt);
257 static int _host_ok (pam_ldap_session_t * session);
258 static int _service_ok (pam_handle_t * handle, pam_ldap_session_t * session);
259 static char *_get_md5_salt (char saltbuf[16]);
260 static char *_get_salt (char salt[16]);
261 static int _escape_string (const char *str, char *buf, size_t buflen);
262 static int _get_user_info (pam_ldap_session_t * session, const char *user);
263 static int _pam_ldap_get_session (pam_handle_t * pamh, const char *username,
264 				  const char *configFile,
265 				  pam_ldap_session_t ** psession);
266 static int _session_reopen (pam_ldap_session_t * session);
267 static int _get_password_policy (pam_ldap_session_t * session,
268 				 pam_ldap_password_policy_t * policy);
269 static int _do_authentication (pam_handle_t *pamh, pam_ldap_session_t * session,
270 			       const char *user, const char *password);
271 static int _update_authtok (pam_handle_t *pamh,
272 			    pam_ldap_session_t * session,
273 			    const char *user,
274 			    const char *old_password,
275 			    const char *new_password);
276 static int _get_authtok (pam_handle_t * pamh, int flags, int first);
277 static int _conv_sendmsg (struct pam_conv *aconv,
278 			  const char *message, int style, int no_warn);
279 
280 #if defined(HAVE_LIBPTHREAD) || defined(HAVE_LDAPSSL_INIT)
281 #include <dlfcn.h>
282 #endif
283 
284 #ifdef HAVE_LIBPTHREAD
285 
286 /*
287  * on Linux at least, the pthread library registers an atexit
288  * handler in it's constructor.  Since we are in a library and linking with
289  * libpthread, if the client program is not linked with libpthread, it
290  * segfaults on exit. So we open an extra reference to the library.
291  *
292  * If there is a better way of doing this, let us know.
293  */
294 #ifdef __GNUC__
295 void nasty_pthread_hack (void) __attribute__ ((constructor));
296 #else
297 # ifdef __SUNPRO_C
298 #  pragma init(nasty_pthread_hack)
299 # endif				/* __SUNPRO_C */
300 #endif /* __GNUC__ */
301 
302 void
nasty_pthread_hack(void)303 nasty_pthread_hack (void)
304 {
305   (void) dlopen ("libpthread.so", RTLD_LAZY);
306 }
307 #endif /* HAVE_LIBPTHREAD */
308 
309 #ifdef HAVE_LDAPSSL_INIT
310 /*
311  * We need to keep ourselves loaded so that ssl_initialized
312  * is set across PAM sessions.
313  */
314 #ifdef __GNUC__
315 void nasty_ssl_hack (void) __attribute__ ((constructor));
316 #else
317 # ifdef __SUNPRO_C
318 #  pragma init(nasty_ssl_hack)
319 # endif				/* __SUNPRO_C */
320 #endif /* __GNUC__ */
321 
322 void
nasty_ssl_hack(void)323 nasty_ssl_hack (void)
324 {
325   (void) dlopen ("/lib/security/pam_ldap.so", RTLD_LAZY);
326 }
327 #endif /* HAVE_LDAPSSL_INIT */
328 
329 /* i64c - convert an integer to a radix 64 character */
330 static int
i64c(int i)331 i64c (int i)
332 {
333   const char *base64 =
334     "./01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
335   if (i < 0)
336     i = 0;
337   else if (i > 63)
338     i = 63;
339 
340   return base64[i];
341 }
342 
343 #ifndef HAVE_LDAP_GET_LDERRNO
344 static int
ldap_get_lderrno(LDAP * ld,char ** m,char ** s)345 ldap_get_lderrno (LDAP * ld, char **m, char **s)
346 {
347 #ifdef HAVE_LDAP_GET_OPTION
348   int rc;
349 #endif
350   int lderrno;
351 
352 #if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER)
353   /* is this needed? */
354   rc = ldap_get_option (ld, LDAP_OPT_ERROR_NUMBER, &lderrno);
355   if (rc != LDAP_SUCCESS)
356     return rc;
357 #else
358   lderrno = ld->ld_errno;
359 #endif
360 
361   if (s != NULL)
362     {
363 #if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_STRING)
364       rc = ldap_get_option (ld, LDAP_OPT_ERROR_STRING, s);
365       if (rc != LDAP_SUCCESS)
366 	return rc;
367 #else
368       *s = ld->ld_error;
369 #endif
370     }
371 
372   if (m != NULL)
373     {
374 #if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_MATCHED_DN)
375       rc = ldap_get_option (ld, LDAP_OPT_MATCHED_DN, m);
376       if (rc != LDAP_SUCCESS)
377 	return rc;
378 #else
379       *m = ld->ld_matched;
380 #endif
381     }
382 
383   return lderrno;
384 }
385 #endif
386 
387 #ifndef HAVE_LDAP_SET_LDERRNO
388 static int
ldap_set_lderrno(LDAP * ld,int lderrno,const char * m,const char * s)389 ldap_set_lderrno (LDAP * ld, int lderrno, const char *m, const char *s)
390 {
391 #ifdef HAVE_LDAP_SET_OPTION
392   int rc;
393 #endif
394 
395 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER)
396   /* is this needed? */
397   rc = ldap_set_option (ld, LDAP_OPT_ERROR_NUMBER, &lderrno);
398   if (rc != LDAP_SUCCESS)
399     return rc;
400 #else
401   ld->ld_errno = lderrno;
402 #endif
403 
404   if (s != NULL)
405     {
406 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_ERROR_STRING)
407       rc = ldap_set_option (ld, LDAP_OPT_ERROR_STRING, s);
408       if (rc != LDAP_SUCCESS)
409 	return rc;
410 #else
411       ld->ld_error = s;
412 #endif
413     }
414 
415   if (m != NULL)
416     {
417 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_MATCHED_DN)
418       rc = ldap_set_option (ld, LDAP_OPT_MATCHED_DN, m);
419       if (rc != LDAP_SUCCESS)
420 	return rc;
421 #else
422       ld->ld_matched = m;
423 #endif
424     }
425 
426   return LDAP_SUCCESS;
427 }
428 #endif
429 
430 static void
_release_config(pam_ldap_config_t ** pconfig)431 _release_config (pam_ldap_config_t ** pconfig)
432 {
433   pam_ldap_config_t *c;
434 
435   c = *pconfig;
436   if (c == NULL)
437     return;
438 
439   if (c->configFile != NULL)
440     free (c->configFile);
441 
442   if (c->host != NULL)
443     free (c->host);
444 
445   if (c->base != NULL)
446     free (c->base);
447 
448   if (c->binddn != NULL)
449     free (c->binddn);
450 
451   if (c->bindpw != NULL)
452     {
453       _pam_overwrite (c->bindpw);
454       _pam_drop (c->bindpw);
455     }
456 
457   if (c->rootbinddn != NULL)
458     free (c->rootbinddn);
459 
460   if (c->rootbindpw != NULL)
461     {
462       _pam_overwrite (c->rootbindpw);
463       _pam_drop (c->rootbindpw);
464     }
465 
466   if (c->sslpath != NULL)
467     {
468       free (c->sslpath);
469     }
470 
471   if (c->userattr != NULL)
472     {
473       free (c->userattr);
474     }
475 
476   if (c->tmplattr != NULL)
477     {
478       free (c->tmplattr);
479     }
480 
481   if (c->tmpluser != NULL)
482     {
483       free (c->tmpluser);
484     }
485 
486   if (c->groupattr != NULL)
487     {
488       free (c->groupattr);
489     }
490 
491   if (c->groupdn != NULL)
492     {
493       free (c->groupdn);
494     }
495 
496   if (c->filter != NULL)
497     {
498       free (c->filter);
499     }
500 
501   if (c->logdir != NULL)
502     {
503       free (c->logdir);
504     }
505 
506   if (c->sasl_mechanism != NULL)
507     {
508       free (c->sasl_mechanism);
509     }
510 
511   if (c->password_prohibit_message != NULL)
512     {
513       free (c->password_prohibit_message);
514     }
515 
516   memset (c, 0, sizeof (*c));
517   free (c);
518   *pconfig = NULL;
519 
520   return;
521 }
522 
523 static void
_release_user_info(pam_ldap_user_info_t ** info)524 _release_user_info (pam_ldap_user_info_t ** info)
525 {
526   if (*info == NULL)
527     return;
528 
529   if ((*info)->userdn != NULL)
530     {
531       ldap_memfree ((void *) (*info)->userdn);
532     }
533 
534   /*
535    * Clobber the password.
536    */
537   _pam_overwrite ((*info)->userpw);
538   _pam_drop ((*info)->userpw);
539 
540   if ((*info)->hosts_allow != NULL)
541     {
542       ldap_value_free ((*info)->hosts_allow);
543     }
544 
545   if ((*info)->services_allow != NULL)
546     {
547       ldap_value_free ((*info)->services_allow);
548     }
549 
550   if ((*info)->tmpluser != NULL)
551     {
552       free ((void *) (*info)->tmpluser);
553     }
554 
555   free ((void *) (*info)->username);
556   free (*info);
557 
558   *info = NULL;
559   return;
560 }
561 
562 static void
_pam_ldap_cleanup_session(pam_handle_t * pamh,void * data,int error_status)563 _pam_ldap_cleanup_session (pam_handle_t * pamh, void *data, int error_status)
564 {
565   pam_ldap_session_t *session = (pam_ldap_session_t *) data;
566 
567   if (session == NULL)
568     return;
569 
570   if (session->ld != NULL)
571     {
572       ldap_unbind (session->ld);
573       session->ld = NULL;
574     }
575 
576   _release_config (&session->conf);
577   _release_user_info (&session->info);
578 
579   free (data);
580 #if LDAP_SET_REBIND_PROC_ARGS < 3
581   global_session = 0;
582 #endif
583 
584   return;
585 }
586 
587 static void
_cleanup_data(pam_handle_t * pamh,void * data,int error_status)588 _cleanup_data (pam_handle_t * pamh, void *data, int error_status)
589 {
590   if (data != NULL)
591     free (data);
592 
593   return;
594 }
595 
596 static void
_cleanup_authtok_data(pam_handle_t * pamh,void * data,int error_status)597 _cleanup_authtok_data (pam_handle_t * pamh, void *data, int error_status)
598 {
599   _pam_overwrite ((char *) data);
600   _pam_drop (data);
601 
602   return;
603 }
604 
605 static int
_alloc_config(pam_ldap_config_t ** presult)606 _alloc_config (pam_ldap_config_t ** presult)
607 {
608   pam_ldap_config_t *result;
609 
610   if (*presult == NULL)
611     {
612       *presult = (pam_ldap_config_t *) calloc (1, sizeof (*result));
613       if (*presult == NULL)
614 	return PAM_BUF_ERR;
615     }
616 
617   result = *presult;
618 
619   result->scope = LDAP_SCOPE_SUBTREE;
620   result->deref = LDAP_DEREF_NEVER;
621   result->configFile = NULL;
622   result->host = NULL;
623   result->base = NULL;
624   result->port = 0;
625   result->binddn = NULL;
626   result->bindpw = NULL;
627   result->rootbinddn = NULL;
628   result->rootbindpw = NULL;
629   result->ssl_on = SSL_OFF;
630   result->sslpath = NULL;
631   result->filter = NULL;
632   result->ssd = NULL;
633   result->userattr = NULL;
634   result->groupattr = NULL;
635   result->groupdn = NULL;
636   result->getpolicy = 0;
637   result->checkhostattr = 0;
638   result->checkserviceattr = 0;
639 #ifdef LDAP_VERSION3
640   result->version = LDAP_VERSION3;
641 #else
642   result->version = LDAP_VERSION2;
643 #endif /* LDAP_VERSION2 */
644   result->timelimit = LDAP_NO_LIMIT;
645   result->bind_timelimit = 10;
646   result->referrals = 1;
647   result->restart = 1;
648   result->password_type = PASSWORD_CLEAR;
649   result->min_uid = 0;
650   result->max_uid = 0;
651   result->tmplattr = NULL;
652   result->tmpluser = NULL;
653   result->tls_checkpeer = -1;
654   result->tls_cacertfile = NULL;
655   result->tls_cacertdir = NULL;
656   result->tls_ciphers = NULL;
657   result->tls_cert = NULL;
658   result->tls_key = NULL;
659   result->tls_randfile = NULL;
660   result->logdir = NULL;
661   result->sasl_mechanism = NULL;
662   result->debug = 0;
663   return PAM_SUCCESS;
664 }
665 
666 
667 #ifdef YPLDAPD
668 /*
669  * Use the "internal" ypldapd.conf map to figure some things
670  * out.
671  */
672 static int
_ypldapd_read_config(pam_ldap_config_t ** presult)673 _ypldapd_read_config (pam_ldap_config_t ** presult)
674 {
675   pam_ldap_config_t *result;
676   char *domain;
677   int len;
678   char *tmp;
679 
680   if (_alloc_config (presult) != PAM_SUCCESS)
681     {
682       return PAM_BUF_ERR;
683     }
684 
685   result = *presult;
686 
687   yp_get_default_domain (&domain);
688   yp_bind (domain);
689   if (yp_match (domain,
690 		"ypldapd.conf",
691 		"ldaphost", sizeof ("ldaphost") - 1, &tmp, &len))
692     {
693       return PAM_SERVICE_ERR;
694     }
695 
696   result->host = (char *) malloc (len + 1);
697   if (result->host == NULL)
698     return PAM_BUF_ERR;
699 
700   memcpy (result->host, tmp, len);
701   result->host[len] = '\0';
702   free (tmp);
703 
704   if (yp_match (domain,
705 		"ypldapd.conf", "basedn", sizeof ("basedn") - 1, &tmp, &len))
706     {
707       result->base = NULL;
708     }
709   else
710     {
711       result->base = (char *) malloc (len + 1);
712       if (result->base == NULL)
713 	return PAM_BUF_ERR;
714       memcpy (result->base, tmp, len);
715       result->base[len] = '\0';
716       free (tmp);
717     }
718 
719   if (yp_match (domain,
720 		"ypldapd.conf",
721 		"ldapport", sizeof ("ldapport") - 1, &tmp, &len))
722     {
723       result->port = LDAP_PORT;
724     }
725   else
726     {
727       char *p = (char *) malloc (len + 1);
728       if (p == NULL)
729 	return PAM_BUF_ERR;
730       memcpy (p, tmp, len);
731       result->port = atoi (p);
732       free (tmp);
733       free (p);
734     }
735 
736   yp_unbind (domain);
737 
738   result->userattr = strdup ("uid");
739   if (result->userattr == NULL)
740     {
741       return PAM_BUF_ERR;
742     }
743 
744   /* turn on getting policies */
745   result->getpolicy = 1;
746 #ifdef LDAP_VERSION3
747   result->version = LDAP_VERSION3;
748 #endif
749 
750   return PAM_SUCCESS;
751 }
752 #endif /* YPLDAPD */
753 
754 #define CHECKPOINTER(ptr) do { if ((ptr) == NULL) { \
755     fclose(fp); \
756     return PAM_BUF_ERR; \
757 } \
758 } while (0)
759 
760 static int
_read_config(const char * configFile,pam_ldap_config_t ** presult)761 _read_config (const char *configFile, pam_ldap_config_t ** presult)
762 {
763   /* this is the same configuration file as nss_ldap */
764   FILE *fp;
765   char b[BUFSIZ];
766   pam_ldap_config_t *result;
767 
768   if (_alloc_config (presult) != PAM_SUCCESS)
769     {
770       return PAM_BUF_ERR;
771     }
772 
773   result = *presult;
774 
775   /* configuration file location is configurable; default /etc/ldap.conf */
776   if (configFile == NULL)
777     {
778       configFile = PAM_LDAP_PATH_CONF;
779       result->configFile = NULL;
780     }
781   else
782     {
783       result->configFile = strdup (configFile);
784       if (result->configFile == NULL)
785 	return PAM_BUF_ERR;
786     }
787 
788   fp = fopen (configFile, "r");
789 
790   if (fp == NULL)
791     {
792       /*
793        * According to PAM Documentation, such an error in a config file
794        * SHOULD be logged at LOG_ALERT level
795        */
796       syslog (LOG_ALERT, "pam_ldap: missing file \"%s\"", configFile);
797       return PAM_SERVICE_ERR;
798     }
799 
800   result->scope = LDAP_SCOPE_SUBTREE;
801 
802   while (fgets (b, sizeof (b), fp) != NULL)
803     {
804       char *k, *v;
805       int len;
806 
807       if (*b == '\n' || *b == '#')
808 	continue;
809 
810       k = b;
811       v = k;
812       while (*v != '\0' && *v != ' ' && *v != '\t')
813 	v++;
814 
815       if (*v == '\0')
816 	continue;
817 
818       *(v++) = '\0';
819 
820       /* skip all whitespaces between keyword and value */
821       /* Lars Oergel <lars.oergel@innominate.de>, 05.10.2000 */
822       while (*v == ' ' || *v == '\t')
823 	v++;
824 
825       /* kick off all whitespaces and newline at the end of value */
826       /* Bob Guo <bob@mail.ied.ac.cn>, 08.10.2001 */
827       len = strlen (v) - 1;
828       while (v[len] == ' ' || v[len] == '\t' || v[len] == '\n')
829 	--len;
830       v[len + 1] = '\0';
831 
832       if (!strcasecmp (k, "host"))
833 	{
834 	  CHECKPOINTER (result->host = strdup (v));
835 	}
836       else if (!strcasecmp (k, "uri"))
837 	{
838 	  CHECKPOINTER (result->uri = strdup (v));
839 	}
840       else if (!strcasecmp (k, "base"))
841 	{
842 	  CHECKPOINTER (result->base = strdup (v));
843 	}
844       else if (!strcasecmp (k, "binddn"))
845 	{
846 	  CHECKPOINTER (result->binddn = strdup (v));
847 	}
848       else if (!strcasecmp (k, "bindpw"))
849 	{
850 	  CHECKPOINTER (result->bindpw = strdup (v));
851 	}
852       else if (!strcasecmp (k, "rootbinddn"))
853 	{
854 	  CHECKPOINTER (result->rootbinddn = strdup (v));
855 	}
856       else if (!strcasecmp (k, "scope"))
857 	{
858 	  if (!strncasecmp (v, "sub", 3))
859 	    result->scope = LDAP_SCOPE_SUBTREE;
860 	  else if (!strncasecmp (v, "one", 3))
861 	    result->scope = LDAP_SCOPE_ONELEVEL;
862 	  else if (!strncasecmp (v, "base", 4))
863 	    result->scope = LDAP_SCOPE_BASE;
864 	}
865       else if (!strcasecmp (k, "deref"))
866 	{
867 	  if (!strcasecmp (v, "never"))
868 	    result->deref = LDAP_DEREF_NEVER;
869 	  else if (!strcasecmp (v, "searching"))
870 	    result->deref = LDAP_DEREF_SEARCHING;
871 	  else if (!strcasecmp (v, "finding"))
872 	    result->deref = LDAP_DEREF_FINDING;
873 	  else if (!strcasecmp (v, "always"))
874 	    result->deref = LDAP_DEREF_ALWAYS;
875 	}
876       else if (!strcasecmp (k, "pam_password"))
877 	{
878 	  if (!strcasecmp (v, "clear"))
879 	    result->password_type = PASSWORD_CLEAR;
880 	  else if (!strcasecmp (v, "crypt"))
881 	    result->password_type = PASSWORD_CRYPT;
882 	  else if (!strcasecmp (v, "md5"))
883 	    result->password_type = PASSWORD_MD5;
884 	  else if (!strcasecmp (v, "clear_remove_old") || !strcasecmp (v, "nds") || (!strcasecmp (v, "racf")))
885 	    result->password_type = PASSWORD_CLEAR_REMOVE_OLD;
886 	  else if (!strcasecmp (v, "ad"))
887 	    result->password_type = PASSWORD_AD;
888 	  else if (!strcasecmp (v, "exop"))
889 	    result->password_type = PASSWORD_EXOP;
890 	  else if (!strcasecmp (v, "exop_send_old"))
891 	    result->password_type = PASSWORD_EXOP_SEND_OLD;
892 	}
893       else if (!strcasecmp (k, "pam_password_prohibit_message"))
894 	{
895 	  CHECKPOINTER (result->password_prohibit_message = strdup (v));
896 	}
897       else if (!strcasecmp (k, "pam_crypt"))
898 	{
899 	  /*
900 	   * we still support this even though it is
901 	   * deprecated, as it could be a security
902 	   * hole to change this behaviour on
903 	   * unsuspecting users of pam_ldap.
904 	   */
905 	  if (!strcasecmp (v, "local"))
906 	    result->password_type = PASSWORD_CRYPT;
907 	  else
908 	    result->password_type = PASSWORD_CLEAR;
909 	}
910       else if (!strcasecmp (k, "port"))
911 	{
912 	  result->port = atoi (v);
913 	}
914       else if (!strcasecmp (k, "timelimit"))
915 	{
916 	  result->timelimit = atoi (v);
917 	}
918       else if (!strcasecmp (k, "bind_timelimit"))
919 	{
920 	  result->bind_timelimit = atoi (v);
921 	}
922       else if (!strcasecmp (k, "nss_base_passwd"))
923 	{
924 	  char *s;
925 	  pam_ssd_t *p, *ssd = calloc (1, sizeof (pam_ssd_t));
926 
927 	  /* this doesn't do any escaping. XXX. */
928 	  s = strchr (v, '?');
929 	  if (s != NULL)
930 	    {
931 	      len = s - v;
932 	      if (s[-1] == ',' && result->base)
933 		{
934 		  ssd->base = malloc (len + strlen (result->base) + 1);
935 		  strncpy (ssd->base, v, len);
936 		  strcpy (ssd->base + len, result->base);
937 		}
938 	      else
939 		{
940 		  ssd->base = malloc (len + 1);
941 		  strncpy (ssd->base, v, len);
942 		  ssd->base[len] = '\0';
943 		}
944 	      s++;
945 	      if (!strncasecmp (s, "sub", 3))
946 		ssd->scope = LDAP_SCOPE_SUBTREE;
947 	      else if (!strncasecmp (s, "one", 3))
948 		ssd->scope = LDAP_SCOPE_ONELEVEL;
949 	      else if (!strncasecmp (s, "base", 4))
950 		ssd->scope = LDAP_SCOPE_BASE;
951 	      s = strchr (s, '?');
952 	      if (s != NULL)
953 		{
954 		  s++;
955 		  CHECKPOINTER (ssd->filter = strdup (s));
956 		}
957 	    }
958 	  else
959 	    {
960 	      ssd->base = strdup (v);
961 	      ssd->scope = LDAP_SCOPE_SUBTREE;
962 	    }
963 
964 	  for (p = result->ssd; p && p->next; p = p->next);
965 	  if (p)
966 	    {
967 	      p->next = ssd;
968 	    }
969 	  else
970 	    {
971 	      result->ssd = ssd;
972 	    }
973 	}
974       else if (!strcasecmp (k, "ldap_version"))
975 	{
976 	  result->version = atoi (v);
977 	}
978       else if (!strcasecmp (k, "sslpath"))
979 	{
980 	  CHECKPOINTER (result->sslpath = strdup (v));
981 	}
982       else if (!strcasecmp (k, "ssl"))
983 	{
984 	  if (!strcasecmp (v, "on") || !strcasecmp (v, "yes")
985 	      || !strcasecmp (v, "true"))
986 	    {
987 	      result->ssl_on = SSL_LDAPS;
988 	    }
989 	  else if (!strcasecmp (v, "start_tls"))
990 	    {
991 	      result->ssl_on = SSL_START_TLS;
992 	    }
993 	}
994       else if (!strcasecmp (k, "referrals"))
995 	{
996 	  result->referrals = (!strcasecmp (v, "on") || !strcasecmp (v, "yes")
997 			       || !strcasecmp (v, "true"));
998 	}
999       else if (!strcasecmp (k, "restart"))
1000 	{
1001 	  result->restart = (!strcasecmp (v, "on") || !strcasecmp (v, "yes")
1002 			     || !strcasecmp (v, "true"));
1003 	}
1004       else if (!strcasecmp (k, "pam_filter"))
1005 	{
1006 	  CHECKPOINTER (result->filter = strdup (v));
1007 	}
1008       else if (!strcasecmp (k, "pam_login_attribute"))
1009 	{
1010 	  CHECKPOINTER (result->userattr = strdup (v));
1011 	}
1012       else if (!strcasecmp (k, "pam_template_login_attribute"))
1013 	{
1014 	  CHECKPOINTER (result->tmplattr = strdup (v));
1015 	}
1016       else if (!strcasecmp (k, "pam_template_login"))
1017 	{
1018 	  CHECKPOINTER (result->tmpluser = strdup (v));
1019 	}
1020       else if (!strcasecmp (k, "pam_lookup_policy"))
1021 	{
1022 	  result->getpolicy = !strcasecmp (v, "yes");
1023 	}
1024       else if (!strcasecmp (k, "pam_check_host_attr"))
1025 	{
1026 	  result->checkhostattr = !strcasecmp (v, "yes");
1027 	}
1028       else if (!strcasecmp (k, "pam_check_service_attr"))
1029 	{
1030 	  result->checkserviceattr = !strcasecmp (v, "yes");
1031 	}
1032       else if (!strcasecmp (k, "pam_groupdn"))
1033 	{
1034 	  CHECKPOINTER (result->groupdn = strdup (v));
1035 	}
1036       else if (!strcasecmp (k, "pam_member_attribute"))
1037 	{
1038 	  CHECKPOINTER (result->groupattr = strdup (v));
1039 	}
1040       else if (!strcasecmp (k, "pam_min_uid"))
1041 	{
1042 	  result->min_uid = (uid_t) atol (v);
1043 	}
1044       else if (!strcasecmp (k, "pam_max_uid"))
1045 	{
1046 	  result->max_uid = (uid_t) atol (v);
1047 	}
1048       else if (!strcasecmp (k, "tls_checkpeer"))
1049 	{
1050 	  if (!strcasecmp (v, "on") || !strcasecmp (v, "yes")
1051 	      || !strcasecmp (v, "true"))
1052 	    {
1053 	      result->tls_checkpeer = 1;	/* LDAP_OPT_X_TLS_HARD */
1054 	    }
1055 	  else if (!strcasecmp (v, "off") || !strcasecmp (v, "no")
1056 		   || !strcasecmp (v, "false"))
1057 	    {
1058 	      result->tls_checkpeer = 0;	/* LDAP_OPT_X_TLS_NEVER */
1059 	    }
1060 	}
1061       else if (!strcasecmp (k, "tls_cacertfile"))
1062 	{
1063 	  CHECKPOINTER (result->tls_cacertfile = strdup (v));
1064 	}
1065       else if (!strcasecmp (k, "tls_cacertdir"))
1066 	{
1067 	  CHECKPOINTER (result->tls_cacertdir = strdup (v));
1068 	}
1069       else if (!strcasecmp (k, "tls_ciphers"))
1070 	{
1071 	  CHECKPOINTER (result->tls_ciphers = strdup (v));
1072 	}
1073       else if (!strcasecmp (k, "tls_cert"))
1074 	{
1075 	  CHECKPOINTER (result->tls_cert = strdup (v));
1076 	}
1077       else if (!strcasecmp (k, "tls_key"))
1078 	{
1079 	  CHECKPOINTER (result->tls_key = strdup (v));
1080 	}
1081       else if (!strcasecmp (k, "tls_randfile"))
1082 	{
1083 	  CHECKPOINTER (result->tls_randfile = strdup (v));
1084 	}
1085       else if (!strcasecmp (k, "logdir"))
1086 	{
1087 	  CHECKPOINTER (result->logdir = strdup (v));
1088 	}
1089       else if (!strcasecmp (k, "pam_sasl_mech"))
1090 	{
1091 	  CHECKPOINTER (result->sasl_mechanism = strdup (v));
1092 	}
1093       else if (!strcasecmp (k, "debug"))
1094 	{
1095 	  result->debug = atol (v);
1096 	}
1097     }
1098 
1099 #ifdef HAVE_LDAP_INITIALIZE
1100   if (result->host == NULL && result->uri == NULL)
1101 #else
1102   if (result->host == NULL)
1103 #endif
1104     {
1105       /*
1106        * According to PAM Documentation, such an error in a config file
1107        * SHOULD be logged at LOG_ALERT level
1108        */
1109       syslog (LOG_ALERT, "pam_ldap: missing \"host\" in file \"%s\"",
1110 	      configFile);
1111       return PAM_SERVICE_ERR;
1112     }
1113 
1114 #if !(defined(HAVE_SASL_SASL_H) || defined(HAVE_SASL_H)) && !defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S)
1115   if (result->sasl_mechanism != NULL)
1116     {
1117       syslog (LOG_ERR, "pam_ldap: SASL mechanism \"%s\" requested, "
1118 	      "but module not built with SASL support", result->sasl_mechanism);
1119       return PAM_SERVICE_ERR;
1120     }
1121 #endif
1122 
1123   if (result->userattr == NULL)
1124     {
1125       CHECKPOINTER (result->userattr = strdup ("uid"));
1126     }
1127 
1128   if (result->groupattr == NULL)
1129     {
1130       CHECKPOINTER (result->groupattr = strdup ("uniquemember"));
1131     }
1132 
1133   if (result->port == 0)
1134     {
1135 #if defined(HAVE_LDAPSSL_INIT) || defined(HAVE_LDAP_START_TLS_S)
1136       if (result->ssl_on == SSL_LDAPS)
1137 	{
1138 	  result->port = LDAPS_PORT;
1139 	}
1140       else
1141 #endif
1142 	result->port = LDAP_PORT;
1143     }
1144 
1145   fclose (fp);
1146 
1147   if ((result->rootbinddn != NULL) && (geteuid () == 0))
1148     {
1149       fp = fopen (PAM_LDAP_PATH_ROOTPASSWD, "r");
1150       if (fp != NULL)
1151 	{
1152 	  if (fgets (b, sizeof (b), fp) != NULL)
1153 	    {
1154 	      int len;
1155 	      len = strlen (b);
1156 	      if (len > 0 && b[len - 1] == '\n')
1157 		len--;
1158 
1159 	      b[len] = '\0';
1160 	      result->rootbindpw = strdup (b);
1161 	    }
1162 	  fclose (fp);
1163 	}
1164       else
1165 	{
1166 	  _pam_drop (result->rootbinddn);
1167 	  syslog (LOG_WARNING,
1168 		  "pam_ldap: could not open secret file %s (%s)",
1169 		  PAM_LDAP_PATH_ROOTPASSWD, strerror (errno));
1170 	}
1171     }
1172 
1173   /* can't use _pam_overwrite because it only goes to end of string,
1174    * not the buffer
1175    */
1176   memset (b, 0, BUFSIZ);
1177   return PAM_SUCCESS;
1178 }
1179 
1180 static int
_open_session(pam_ldap_session_t * session)1181 _open_session (pam_ldap_session_t * session)
1182 {
1183 #ifdef LDAP_X_OPT_CONNECT_TIMEOUT
1184   int timeout;
1185 #endif
1186 #ifdef LDAP_OPT_NETWORK_TIMEOUT
1187   struct timeval tv;
1188 #endif
1189 
1190 #ifdef HAVE_LDAP_SET_OPTION
1191   if (session->conf->debug)
1192     {
1193 #ifdef LBER_OPT_LOG_PRINT_FILE
1194       if (session->conf->logdir && !debugfile)
1195 	{
1196 	  char *name = malloc (strlen (session->conf->logdir) + 18);
1197 	  if (name)
1198 	    {
1199 	      sprintf (name, "%s/ldap.%d", session->conf->logdir,
1200 		       (int) getpid ());
1201 	      debugfile = fopen (name, "a");
1202 	      free (name);
1203 	    }
1204 	  if (debugfile)
1205 	    {
1206 	      ber_set_option (NULL, LBER_OPT_LOG_PRINT_FILE, debugfile);
1207 	    }
1208 	}
1209 #endif
1210       if (session->conf->debug)
1211 	{
1212 #ifdef LBER_OPT_DEBUG_LEVEL
1213 	  ber_set_option (NULL, LBER_OPT_DEBUG_LEVEL, &session->conf->debug);
1214 #endif /* LBER_OPT_DEBUG_LEVEL */
1215 #ifdef LDAP_OPT_DEBUG_LEVEL
1216 	  ldap_set_option (NULL, LDAP_OPT_DEBUG_LEVEL, &session->conf->debug);
1217 #endif /* LDAP_OPT_DEBUG_LEVEL */
1218 	}
1219     }
1220 #endif /* HAVE_LDAP_SET_OPTION */
1221 
1222 #ifdef HAVE_LDAPSSL_INIT
1223   if (session->conf->ssl_on == SSL_LDAPS && ssl_initialized == 0)
1224     {
1225       int rc = ldapssl_client_init (session->conf->sslpath, NULL);
1226       if (rc != LDAP_SUCCESS)
1227 	{
1228 	  syslog (LOG_ERR, "pam_ldap: ldapssl_client_init %s",
1229 		  ldap_err2string (rc));
1230 	  return PAM_SERVICE_ERR;
1231 	}
1232       ssl_initialized = 1;
1233     }
1234 
1235   if (session->conf->ssl_on)
1236     {
1237       session->ld = ldapssl_init (session->conf->host,
1238 				  session->conf->port, TRUE);
1239     }
1240   else
1241 #endif /* HAVE_LDAPSSL_INIT */
1242     {
1243 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS)
1244       /* set defaults for global TLS-related options */
1245       if (_set_ssl_default_options (session) != LDAP_SUCCESS)
1246 	{
1247 	  syslog (LOG_ERR, "pam_ldap: _set_ssl_default_options failed");
1248 	}
1249 #endif
1250 #ifdef HAVE_LDAP_INITIALIZE
1251       if (session->conf->uri != NULL)
1252 	{
1253 	  int rc = ldap_initialize (&session->ld, session->conf->uri);
1254 	  if (rc != LDAP_SUCCESS)
1255 	    {
1256 	      syslog (LOG_ERR, "pam_ldap: ldap_initialize %s",
1257 		      ldap_err2string (rc));
1258 	      return PAM_SERVICE_ERR;
1259 	    }
1260 	}
1261       else
1262 	{
1263 #endif /* HAVE_LDAP_INTITIALIZE */
1264 #ifdef HAVE_LDAP_INIT
1265 	  session->ld = ldap_init (session->conf->host, session->conf->port);
1266 #else
1267 	  session->ld = ldap_open (session->conf->host, session->conf->port);
1268 #endif /* HAVE_LDAP_INIT */
1269 #ifdef HAVE_LDAP_INITIALIZE
1270 	}
1271 #endif /* HAVE_LDAP_INTIALIZE */
1272     }
1273 
1274   if (session->ld == NULL)
1275     {
1276       return PAM_SERVICE_ERR;
1277     }
1278 
1279 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS)
1280   if (session->conf->ssl_on == SSL_LDAPS)
1281     {
1282       int tls = LDAP_OPT_X_TLS_HARD;
1283       int rc = ldap_set_option (session->ld, LDAP_OPT_X_TLS, &tls);
1284       if (rc != LDAP_SUCCESS)
1285 	{
1286 	  syslog (LOG_ERR, "pam_ldap: ldap_set_option(LDAP_OPT_X_TLS) %s",
1287 		  ldap_err2string (rc));
1288 	  return PAM_SERVICE_ERR;
1289 	}
1290 
1291       /* set up SSL per-context settings */
1292       if (_set_ssl_options (session) != LDAP_SUCCESS)
1293 	{
1294 	  syslog (LOG_ERR, "pam_ldap: _set_ssl_options failed");
1295 	}
1296     }
1297 #endif /* LDAP_OPT_X_TLS */
1298 
1299 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_PROTOCOL_VERSION)
1300   (void) ldap_set_option (session->ld, LDAP_OPT_PROTOCOL_VERSION,
1301 			  &session->conf->version);
1302 #else
1303   session->ld->ld_version = session->conf->version;
1304 #endif
1305 
1306 #if LDAP_SET_REBIND_PROC_ARGS == 3
1307   ldap_set_rebind_proc (session->ld, _rebind_proc, (void *) session);
1308 #elif LDAP_SET_REBIND_PROC_ARGS == 2
1309   ldap_set_rebind_proc (session->ld, _rebind_proc);
1310 #endif
1311 
1312 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_DEREF)
1313   (void) ldap_set_option (session->ld, LDAP_OPT_DEREF, &session->conf->deref);
1314 #else
1315   session->ld->ld_deref = session->conf->deref;
1316 #endif
1317 
1318 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_TIMELIMIT)
1319   (void) ldap_set_option (session->ld, LDAP_OPT_TIMELIMIT,
1320 			  &session->conf->timelimit);
1321 #else
1322   session->ld->ld_timelimit = session->conf->timelimit;
1323 #endif
1324 
1325 
1326 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_X_OPT_CONNECT_TIMEOUT)
1327   /*
1328    * This is a new option in the Netscape SDK which sets
1329    * the TCP connect timeout. For want of a better value,
1330    * we use the bind_timelimit to control this.
1331    */
1332   timeout = session->conf->bind_timelimit * 1000;
1333   (void) ldap_set_option (session->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout);
1334 #endif
1335 
1336 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_NETWORK_TIMEOUT)
1337   tv.tv_sec = session->conf->bind_timelimit;
1338   tv.tv_usec = 0;
1339   (void) ldap_set_option (session->ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
1340 #endif
1341 
1342 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_REFERRALS)
1343   (void) ldap_set_option (session->ld, LDAP_OPT_REFERRALS,
1344 			  session->conf->
1345 			  referrals ? LDAP_OPT_ON : LDAP_OPT_OFF);
1346 #endif
1347 
1348 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_RESTART)
1349   (void) ldap_set_option (session->ld, LDAP_OPT_RESTART,
1350 			  session->conf->
1351 			  restart ? LDAP_OPT_ON : LDAP_OPT_OFF);
1352 #endif
1353 
1354 #ifdef HAVE_LDAP_START_TLS_S
1355   if (session->conf->ssl_on == SSL_START_TLS)
1356     {
1357       int version, rc;
1358 
1359       if (ldap_get_option (session->ld, LDAP_OPT_PROTOCOL_VERSION, &version)
1360 	  == LDAP_SUCCESS)
1361 	{
1362 	  if (version < LDAP_VERSION3)
1363 	    {
1364 	      version = LDAP_VERSION3;
1365 	      (void) ldap_set_option (session->ld, LDAP_OPT_PROTOCOL_VERSION,
1366 				      &version);
1367 	    }
1368 
1369 	  /* set up SSL context */
1370 	  if (_set_ssl_options (session) != LDAP_SUCCESS)
1371 	    {
1372 	      syslog (LOG_ERR, "pam_ldap: _set_ssl_options failed");
1373 	    }
1374 
1375 	  rc = ldap_start_tls_s (session->ld, NULL, NULL);
1376 	  if (rc != LDAP_SUCCESS)
1377 	    {
1378 	      syslog (LOG_ERR, "pam_ldap: ldap_starttls_s: %s",
1379 		      ldap_err2string (rc));
1380 	      return PAM_AUTHINFO_UNAVAIL;
1381 	    }
1382 	}
1383     }
1384 #endif /* HAVE_LDAP_START_TLS_S */
1385   return PAM_SUCCESS;
1386 }
1387 
1388 #if defined HAVE_LDAP_START_TLS_S || (defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS))
1389 /* Some global TLS-specific options need to be set before we create our
1390  * session context, so we set them here. */
1391 static int
_set_ssl_default_options(pam_ldap_session_t * session)1392 _set_ssl_default_options (pam_ldap_session_t * session)
1393 {
1394   int rc;
1395 
1396 #ifdef LDAP_OPT_X_TLS_RANDOM_FILE
1397   /* rand file */
1398   if (session->conf->tls_randfile != NULL)
1399     {
1400       rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_RANDOM_FILE,
1401 			    session->conf->tls_randfile);
1402       if (rc != LDAP_SUCCESS)
1403 	{
1404 	  syslog (LOG_ERR,
1405 		  "pam_ldap: ldap_set_option(LDAP_OPT_X_TLS_RANDOM_FILE): %s",
1406 		  ldap_err2string (rc));
1407 	  return LDAP_OPERATIONS_ERROR;
1408 	}
1409     }
1410 #endif /* LDAP_OPT_X_TLS_RANDOM_FILE */
1411 
1412   /* ca cert file */
1413   if (session->conf->tls_cacertfile != NULL)
1414     {
1415       rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE,
1416 			    session->conf->tls_cacertfile);
1417       if (rc != LDAP_SUCCESS)
1418 	{
1419 	  syslog (LOG_ERR,
1420 		  "pam_ldap: ldap_set_option(LDAP_OPT_X_TLS_CACERTFILE): %s",
1421 		  ldap_err2string (rc));
1422 	  return LDAP_OPERATIONS_ERROR;
1423 	}
1424     }
1425 
1426   if (session->conf->tls_cacertdir != NULL)
1427     {
1428       /* ca cert directory */
1429       rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTDIR,
1430 			    session->conf->tls_cacertdir);
1431       if (rc != LDAP_SUCCESS)
1432 	{
1433 	  syslog (LOG_ERR,
1434 		  "pam_ldap: ldap_set_option(LDAP_OPT_X_TLS_CACERTDIR): %s",
1435 		  ldap_err2string (rc));
1436 	  return LDAP_OPERATIONS_ERROR;
1437 	}
1438     }
1439 
1440   if (session->conf->tls_checkpeer > -1)
1441     {
1442       /* require cert? */
1443       rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
1444 			    &session->conf->tls_checkpeer);
1445       if (rc != LDAP_SUCCESS)
1446 	{
1447 	  syslog (LOG_ERR,
1448 		  "pam_ldap: ldap_set_option(LDAP_OPT_X_TLS_REQUIRE_CERT): %s",
1449 		  ldap_err2string (rc));
1450 	  return LDAP_OPERATIONS_ERROR;
1451 	}
1452     }
1453 
1454   if (session->conf->tls_ciphers != NULL)
1455     {
1456       /* set cipher suite, certificate and private key: */
1457       rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CIPHER_SUITE,
1458 			    session->conf->tls_ciphers);
1459       if (rc != LDAP_SUCCESS)
1460 	{
1461 	  syslog (LOG_ERR,
1462 		  "pam_ldap: ldap_set_option(LDAP_OPT_X_TLS_CIPHER_SUITE): %s",
1463 		  ldap_err2string (rc));
1464 	  return LDAP_OPERATIONS_ERROR;
1465 	}
1466     }
1467 
1468   if (session->conf->tls_cert != NULL)
1469     {
1470       rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CERTFILE,
1471 			    session->conf->tls_cert);
1472       if (rc != LDAP_SUCCESS)
1473 	{
1474 	  syslog (LOG_ERR,
1475 		  "pam_ldap: ldap_set_option(LDAP_OPT_X_TLS_CERTFILE): %s",
1476 		  ldap_err2string (rc));
1477 	  return LDAP_OPERATIONS_ERROR;
1478 	}
1479     }
1480 
1481   if (session->conf->tls_key != NULL)
1482     {
1483       rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_KEYFILE,
1484 			    session->conf->tls_key);
1485       if (rc != LDAP_SUCCESS)
1486 	{
1487 	  syslog (LOG_ERR,
1488 		  "pam_ldap: ldap_set_option(LDAP_OPT_X_TLS_KEYFILE): %s",
1489 		  ldap_err2string (rc));
1490 	  return LDAP_OPERATIONS_ERROR;
1491 	}
1492     }
1493 
1494   return LDAP_SUCCESS;
1495 }
1496 
1497 /* Now we can set the per-context TLS-specific options. */
1498 static int
_set_ssl_options(pam_ldap_session_t * session)1499 _set_ssl_options (pam_ldap_session_t * session)
1500 {
1501   return LDAP_SUCCESS;
1502 }
1503 #endif
1504 
1505 static int
_connect_anonymously(pam_ldap_session_t * session)1506 _connect_anonymously (pam_ldap_session_t * session)
1507 {
1508   int rc;
1509   int msgid;
1510   struct timeval timeout;
1511   LDAPMessage *result;
1512   int reconnect = 0;
1513 
1514 retry:
1515   if (reconnect)
1516     {
1517       if (session->ld != NULL)
1518         {
1519           ldap_unbind (session->ld);
1520           session->ld = NULL;
1521         }
1522       syslog(LOG_ERR, "pam_ldap: reconnecting to LDAP server...");
1523     }
1524   if (session->ld == NULL)
1525     {
1526       rc = _open_session (session);
1527       if (rc != PAM_SUCCESS)
1528 	return rc;
1529     }
1530 
1531   if (session->conf->rootbinddn && geteuid () == 0)
1532     {
1533       msgid = ldap_simple_bind (session->ld,
1534 				session->conf->rootbinddn,
1535 				session->conf->rootbindpw);
1536     }
1537   else
1538     {
1539       msgid = ldap_simple_bind (session->ld,
1540 				session->conf->binddn, session->conf->bindpw);
1541     }
1542 
1543   if (msgid == -1)
1544     {
1545       int ld_errno = ldap_get_lderrno (session->ld, 0, 0);
1546 
1547       syslog (LOG_ERR, "pam_ldap: ldap_simple_bind %s",
1548 	      ldap_err2string (ld_errno));
1549       if (ld_errno == LDAP_SERVER_DOWN && !reconnect)
1550         {
1551           reconnect = 1;
1552           goto retry;
1553         }
1554       return PAM_AUTHINFO_UNAVAIL;
1555     }
1556 
1557   timeout.tv_sec = session->conf->bind_timelimit;	/* default 10 */
1558   timeout.tv_usec = 0;
1559   rc = ldap_result (session->ld, msgid, FALSE, &timeout, &result);
1560   if (rc == -1 || rc == 0)
1561     {
1562       int ld_errno = ldap_get_lderrno (session->ld, 0, 0);
1563 
1564       syslog (LOG_ERR, "pam_ldap: ldap_result %s",
1565 	      ldap_err2string (ld_errno));
1566       if (ld_errno == LDAP_SERVER_DOWN && !reconnect)
1567         {
1568           reconnect = 1;
1569           goto retry;
1570         }
1571       return PAM_AUTHINFO_UNAVAIL;
1572     }
1573 
1574 #ifdef HAVE_LDAP_PARSE_RESULT
1575   ldap_parse_result (session->ld, result, &rc, 0, 0, 0, 0, TRUE);
1576 #else
1577   rc = ldap_result2error (session->ld, result, TRUE);
1578 #endif
1579 
1580   if (rc != LDAP_SUCCESS)
1581     {
1582       syslog (LOG_ERR, "pam_ldap: error trying to bind (%s)",
1583 	      ldap_err2string (rc));
1584       return PAM_CRED_INSUFFICIENT;
1585     }
1586 
1587   if (session->info != NULL)
1588     {
1589       session->info->bound_as_user = 0;
1590     }
1591 
1592   return PAM_SUCCESS;
1593 }
1594 
1595 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
1596 #if LDAP_SET_REBIND_PROC_ARGS == 3
1597 static int
_rebind_proc(LDAP * ld,LDAP_CONST char * url,ber_tag_t request,ber_int_t msgid,void * arg)1598 _rebind_proc (LDAP * ld, LDAP_CONST char *url, ber_tag_t request,
1599 	      ber_int_t msgid, void *arg)
1600 #else
1601 static int
1602 _rebind_proc (LDAP * ld, LDAP_CONST char *url, int request, ber_int_t msgid)
1603 #endif
1604 {
1605 #if LDAP_SET_REBIND_PROC_ARGS == 3
1606   pam_ldap_session_t *session = (pam_ldap_session_t *) arg;
1607 #else
1608   /* ugly hack */
1609   pam_ldap_session_t *session = global_session;
1610 #endif
1611   char *who, *cred;
1612   struct timeval timeout;
1613   int rc;
1614 #if defined(HAVE_LDAP_PARSE_RESULT) && defined(HAVE_LDAP_CONTROLS_FREE)
1615   int parserc;
1616   LDAPMessage *result;
1617   LDAPControl **controls;
1618   LDAPControl passwd_policy_req;
1619   LDAPControl *srvctrls[2], **psrvctrls = NULL;
1620   struct berval userpw;
1621 #endif /* HAVE_LDAP_PARSE_RESULT && HAVE_LDAP_CONTROLS_FREE */
1622 
1623   if (session->info != NULL && session->info->bound_as_user == 1)
1624     {
1625       who = session->info->userdn;
1626       cred = session->info->userpw;
1627     }
1628   else
1629     {
1630       if (session->conf->rootbinddn != NULL && geteuid () == 0)
1631 	{
1632 	  who = session->conf->rootbinddn;
1633 	  cred = session->conf->rootbindpw;
1634 	}
1635       else
1636 	{
1637 	  who = session->conf->binddn;
1638 	  cred = session->conf->bindpw;
1639 	}
1640     }
1641 
1642   if (session->conf->ssl_on == SSL_START_TLS)
1643     {
1644       rc = ldap_start_tls_s (session->ld, NULL, NULL);
1645       if (rc != LDAP_SUCCESS)
1646         {
1647           syslog (LOG_ERR, "pam_ldap: ldap_starttls_s: %s",
1648                   ldap_err2string (rc));
1649           return LDAP_OPERATIONS_ERROR;
1650         }
1651     }
1652 
1653 #if !defined(HAVE_LDAP_PARSE_RESULT) || !defined(HAVE_LDAP_CONTROLS_FREE)
1654   return ldap_simple_bind_s (ld, who, cred);
1655 #else
1656 #if !defined(HAVE_LDAP_SASL_BIND) || !defined(LDAP_SASL_SIMPLE)
1657   msgid = ldap_simple_bind (ld, who, cred);
1658 #else
1659   if (session->conf->getpolicy)
1660     {
1661       passwd_policy_req.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
1662       passwd_policy_req.ldctl_value.bv_val = 0;	/* none */
1663       passwd_policy_req.ldctl_value.bv_len = 0;
1664       passwd_policy_req.ldctl_iscritical = 0;	/* not critical */
1665       srvctrls[0] = &passwd_policy_req;
1666       srvctrls[1] = 0;
1667 
1668       psrvctrls = srvctrls;
1669     }
1670   userpw.bv_val = cred;
1671   userpw.bv_len = (userpw.bv_val != 0) ? strlen (userpw.bv_val) : 0;
1672 
1673   rc =
1674     ldap_sasl_bind (session->ld, who, LDAP_SASL_SIMPLE,
1675                     &userpw, psrvctrls, 0, &msgid);
1676   if (rc != LDAP_SUCCESS )
1677     {
1678       return rc;
1679     }
1680 #endif
1681   if (msgid == -1)
1682     {
1683       syslog (LOG_ERR, "pam_ldap: ldap_simple_bind %s",
1684               ldap_err2string (ldap_get_lderrno (ld, 0, 0)));
1685       return LDAP_OPERATIONS_ERROR;
1686     }
1687 
1688   timeout.tv_sec = session->conf->bind_timelimit;
1689   timeout.tv_usec = 0;
1690   result = NULL;
1691   rc = ldap_result (ld, msgid, FALSE, &timeout, &result);
1692   if (rc == -1 || rc == 0)
1693     {
1694       syslog (LOG_ERR, "pam_ldap: ldap_result %s",
1695               ldap_err2string (ldap_get_lderrno (ld, 0, 0)));
1696       ldap_msgfree (result);
1697       return LDAP_OPERATIONS_ERROR;
1698     }
1699 
1700   controls = NULL;
1701   parserc = ldap_parse_result (ld, result, &rc, 0, 0, 0, &controls, TRUE);
1702   if (parserc != LDAP_SUCCESS)
1703     {
1704       syslog (LOG_ERR, "pam_ldap: ldap_parse_result %s",
1705               ldap_err2string (parserc));
1706       _pam_overwrite (session->info->userpw);
1707       _pam_drop (session->info->userpw);
1708       return PAM_SERVICE_ERR;
1709     }
1710   if (controls != NULL)
1711     {
1712       LDAPControl **ctlp;
1713       for (ctlp = controls; *ctlp != NULL; ctlp++)
1714         {
1715           if (!strcmp ((*ctlp)->ldctl_oid, LDAP_CONTROL_PWEXPIRED))
1716             {
1717               if (session->info->policy_error == POLICY_ERROR_SUCCESS)
1718                 session->info->policy_error = POLICY_ERROR_PASSWORD_EXPIRED;
1719             }
1720           else if (!strcmp ((*ctlp)->ldctl_oid, LDAP_CONTROL_PASSWORDPOLICYRESPONSE))
1721             _get_password_policy_response_value (&(*ctlp)->ldctl_value,
1722                                                  session);
1723         }
1724       ldap_controls_free (controls);
1725       /* suppress a failure due to password expiration or needs-changing if we
1726        * appear to be in the middle of changing a password */
1727       switch (request)
1728         {
1729         case LDAP_REQ_MODIFY:
1730         case LDAP_REQ_EXTENDED:
1731           switch (session->info->policy_error)
1732             {
1733             case POLICY_ERROR_PASSWORD_EXPIRED:
1734             case POLICY_ERROR_CHANGE_AFTER_RESET:
1735               rc = LDAP_SUCCESS;
1736               break;
1737             default:
1738               break;
1739             }
1740         default:
1741           break;
1742         }
1743     }
1744     return rc;
1745 #endif
1746 }
1747 #else
1748 #if LDAP_SET_REBIND_PROC_ARGS == 3
1749 static int
_rebind_proc(LDAP * ld,char ** whop,char ** credp,int * methodp,int freeit,void * arg)1750 _rebind_proc (LDAP * ld,
1751 	      char **whop, char **credp, int *methodp, int freeit, void *arg)
1752 #else
1753 static int
1754 _rebind_proc (LDAP * ld, char **whop, char **credp, int *methodp, int freeit)
1755 #endif
1756 {
1757 #if LDAP_SET_REBIND_PROC_ARGS == 3
1758   pam_ldap_session_t *session = (pam_ldap_session_t *) arg;
1759 #else
1760   /* ugly hack */
1761   pam_ldap_session_t *session = global_session;
1762 #endif
1763 
1764   if (freeit)
1765     {
1766       _pam_drop (*whop);
1767       _pam_overwrite (*credp);
1768       _pam_drop (*credp);
1769       return LDAP_SUCCESS;
1770     }
1771 
1772   if (session->info != NULL && session->info->bound_as_user == 1)
1773     {
1774       /*
1775        * We're authenticating as a user.
1776        */
1777       *whop = strdup (session->info->userdn);
1778       *credp = strdup (session->info->userpw);
1779     }
1780   else
1781     {
1782       if (session->conf->rootbinddn != NULL && geteuid () == 0)
1783 	{
1784 	  *whop = strdup (session->conf->rootbinddn);
1785 	  *credp = session->conf->rootbindpw != NULL ?
1786 	    strdup (session->conf->rootbindpw) : NULL;
1787 	}
1788       else
1789 	{
1790 	  *whop = session->conf->binddn != NULL ?
1791 	    strdup (session->conf->binddn) : NULL;
1792 	  *credp = session->conf->bindpw != NULL ?
1793 	    strdup (session->conf->bindpw) : NULL;
1794 	}
1795     }
1796 
1797   *methodp = LDAP_AUTH_SIMPLE;
1798 
1799   return LDAP_SUCCESS;
1800 }
1801 #endif
1802 
1803 /*
1804  * See Internet Draft "Password Policy for LDAP Directories".
1805  * draft-behera-ldap-password-policy-07.txt
1806  */
1807 static int
_get_password_policy_response_value(struct berval * response_value,pam_ldap_session_t * session)1808 _get_password_policy_response_value (struct berval *response_value,
1809 				     pam_ldap_session_t * session)
1810 {
1811   char *opaque;
1812   BerElement *ber;
1813   unsigned long tag;
1814   unsigned long len;
1815   int rc = LDAP_SUCCESS;
1816 
1817   if (!response_value || !session)
1818     return LDAP_PARAM_ERROR;
1819 
1820   /* create a BerElement from the berval returned in the control */
1821   ber = ber_init (response_value);
1822   if (ber == NULL)
1823     return LDAP_LOCAL_ERROR;
1824 
1825   /* parse the PasswordPolicyResponseValue */
1826   for (tag = ber_first_element (ber, &len, &opaque);
1827        tag != LBER_DEFAULT; tag = ber_next_element (ber, &len, opaque))
1828     {
1829       unsigned long ttag;
1830       int error;
1831       int value;
1832 
1833       if (tag == 160)		/* warning [0] CHOICE { ... } */
1834 	{
1835 	  if (ber_skip_tag (ber, &len) == 160)
1836 	    {
1837 	      ttag = ber_peek_tag (ber, &len);
1838 	      switch (ttag)
1839 		{
1840 		case POLICY_WARN_TIME_BEFORE_EXPIRATION:
1841 		case POLICY_WARN_GRACE_LOGINS_REMAINING:
1842 		  if (ber_scanf (ber, "i", &value) != LBER_ERROR)
1843 		    {
1844 		      if (ttag == POLICY_WARN_TIME_BEFORE_EXPIRATION)
1845 			session->info->password_expiration_time = value;
1846 		      else
1847 			session->info->grace_logins_remaining = value;
1848 		      continue;
1849 		    }
1850 		}
1851 	    }
1852 	}
1853       else if (tag == 129)	/* error [1] ENUMERATED { ... } */
1854 	{
1855 	  ttag = ber_scanf (ber, "e", &error);
1856 	  if (ttag != LBER_ERROR)
1857 	    {
1858 	      if (session->info->policy_error == POLICY_ERROR_SUCCESS)
1859 		session->info->policy_error = error;
1860 	      continue;
1861 	    }
1862 	}
1863       rc = LDAP_DECODING_ERROR;
1864       break;
1865     }
1866 
1867   ber_free (ber, 1);
1868   return rc;
1869 }
1870 
1871 #if (defined(HAVE_SASL_SASL_H) || defined(HAVE_SASL_H)) && defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S)
1872 /*
1873  * Assign a single value as a result of a SASL interaction
1874  */
1875 static int
_do_sasl_assign_cb(sasl_interact_t * interact,const char * dflt)1876 _do_sasl_assign_cb (sasl_interact_t *interact, const char *dflt)
1877 {
1878   const char *result;
1879 
1880   if (dflt != NULL)
1881     result = dflt;
1882   else if (interact->defresult != NULL)
1883     result = interact->defresult;
1884   else
1885     result = "";
1886 
1887 #if SASL_VERSION_MAJOR < 2
1888   interact->result = strdup (result);
1889   if (interact->result == NULL)
1890     {
1891       return LDAP_NO_MEMORY;
1892     }
1893 #else
1894   interact->result = result;
1895 #endif
1896 
1897   interact->len = strlen(interact->result);
1898 
1899   return LDAP_SUCCESS;
1900 }
1901 
1902 /*
1903  * Provide a value to the SASL layer based on pam_ldap defaults or
1904  * interaction with the user via the application-supplied conversation
1905  * function
1906  */
1907 static int
_do_sasl_interaction(pam_handle_t * pamh,pam_ldap_session_t * session,unsigned flags,sasl_interact_t * interact)1908 _do_sasl_interaction (pam_handle_t *pamh, pam_ldap_session_t *session,
1909 		      unsigned flags, sasl_interact_t *interact)
1910 {
1911   int rc;
1912   const char *dflt = NULL;
1913 
1914   switch (interact->id)
1915     {
1916       case SASL_CB_AUTHNAME:
1917 	dflt = session->info->username;
1918 	break;
1919       case SASL_CB_PASS:
1920 	dflt = session->info->userpw;
1921 	break;
1922       default:
1923 	dflt = NULL;
1924 	break;
1925     }
1926 
1927   if (dflt != NULL && dflt[0] == '\0')
1928     dflt = NULL;
1929 
1930   if (dflt == NULL &&
1931 #ifdef LDAP_SASL_QUIET
1932       flags != LDAP_SASL_QUIET &&
1933 #endif
1934       (interact->id == SASL_CB_ECHOPROMPT || interact->id == SASL_CB_NOECHOPROMPT))
1935     {
1936       struct pam_message *pmsg[2];
1937       struct pam_message challenge_msg;
1938       struct pam_message prompt_msg;
1939       struct pam_response *resp = NULL;
1940       struct pam_conv *conv;
1941       int i = 0;
1942 
1943       if (interact->challenge != NULL)
1944 	{
1945 	  challenge_msg.msg_style = PAM_TEXT_INFO;
1946 	  challenge_msg.msg = (char *)interact->challenge;
1947 	  pmsg[i++] = &challenge_msg;
1948 	}
1949 
1950       prompt_msg.msg_style = (interact->id == SASL_CB_ECHOPROMPT) ?
1951 			     PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF;
1952       prompt_msg.msg = (interact->prompt != NULL) ? (char *)interact->prompt : "Enter SASL response: ";
1953       pmsg[i++] = &prompt_msg;
1954 
1955       rc = pam_get_item(pamh, PAM_CONV, (CONST_ARG void **)&conv);
1956       if (rc != PAM_SUCCESS)
1957 	return LDAP_OTHER;
1958 
1959       rc = conv->conv (i,
1960 		(CONST_ARG struct pam_message **)pmsg,
1961 		&resp, conv->appdata_ptr);
1962       if (rc != PAM_SUCCESS || resp == NULL)
1963 	return LDAP_OTHER;
1964 
1965       /* XXX leaks with SASL v2 */
1966       dflt = resp->resp;
1967       free (resp);
1968     }
1969 
1970   rc = _do_sasl_assign_cb (interact, dflt);
1971 
1972   return rc;
1973 }
1974 
1975 static int
_do_sasl_interact(LDAP * ld,unsigned flags,void * defaults,void * _interact)1976 _do_sasl_interact (LDAP *ld, unsigned flags, void *defaults, void *_interact)
1977 {
1978   sasl_interact_t *interact = (sasl_interact_t *)_interact;
1979   void **args = (void **)defaults;
1980   int rc;
1981 
1982   while (interact->id != SASL_CB_LIST_END)
1983     {
1984       rc = _do_sasl_interaction((pam_handle_t *)args[0], (pam_ldap_session_t *)args[1], flags, interact);
1985       if (rc != LDAP_SUCCESS)
1986 	return rc;
1987 
1988       interact++;
1989     }
1990 
1991   return LDAP_SUCCESS;
1992 }
1993 #endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */
1994 
1995 
1996 static int
_connect_as_user(pam_handle_t * pamh,pam_ldap_session_t * session,const char * password)1997 _connect_as_user (pam_handle_t * pamh, pam_ldap_session_t * session, const char *password)
1998 {
1999   int rc, msgid;
2000   struct timeval timeout;
2001 #if defined(HAVE_LDAP_PARSE_RESULT) && defined(HAVE_LDAP_CONTROLS_FREE)
2002   int parserc;
2003   LDAPMessage *result;
2004   LDAPControl **controls;
2005   LDAPControl passwd_policy_req;
2006   LDAPControl *srvctrls[2], **psrvctrls = NULL;
2007   struct berval userpw;
2008   int reconnect = 0;
2009 #endif /* HAVE_LDAP_PARSE_RESULT && HAVE_LDAP_CONTROLS_FREE */
2010 
2011   /* avoid binding anonymously with a DN but no password */
2012   if (password == NULL || password[0] == '\0')
2013     return PAM_AUTH_ERR;
2014 
2015   /* this shouldn't ever happen */
2016   if (session->info == NULL)
2017     return PAM_SYSTEM_ERR;
2018 
2019   /* if we already bound as the user don't bother retrying */
2020   if (session->info->bound_as_user)
2021     return PAM_SUCCESS;
2022 
2023 retry:
2024   if (reconnect)
2025     {
2026       if (session->ld != NULL)
2027         {
2028           ldap_unbind (session->ld);
2029           session->ld = NULL;
2030         }
2031       session->info->bound_as_user = 0;
2032       syslog(LOG_INFO, "pam_ldap: reconnecting to LDAP server...");
2033     }
2034 
2035   if (session->ld == NULL)
2036     {
2037       rc = _open_session (session);
2038       if (rc != PAM_SUCCESS)
2039 	return rc;
2040     }
2041 
2042   /*
2043    * We copy the password temporarily so that when referrals are
2044    * chased, the correct credentials are set by the rebind
2045    * procedure.
2046    */
2047   if (session->info->userpw != NULL)
2048     {
2049       _pam_overwrite (session->info->userpw);
2050       _pam_drop (session->info->userpw);
2051     }
2052 
2053   session->info->userpw = strdup (password);
2054   if (session->info->userpw == NULL)
2055     return PAM_BUF_ERR;
2056 
2057 #if defined(HAVE_LDAP_PARSE_RESULT) && defined(HAVE_LDAP_CONTROLS_FREE)
2058   if (session->conf->getpolicy)
2059     {
2060       passwd_policy_req.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
2061       passwd_policy_req.ldctl_value.bv_val = 0;	/* none */
2062       passwd_policy_req.ldctl_value.bv_len = 0;
2063       passwd_policy_req.ldctl_iscritical = 0;	/* not critical */
2064       srvctrls[0] = &passwd_policy_req;
2065       srvctrls[1] = 0;
2066 
2067       psrvctrls = srvctrls;
2068     }
2069 #endif
2070 
2071 #if (defined(HAVE_SASL_SASL_H) || defined(HAVE_SASL_H)) && defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S)
2072   if (session->conf->sasl_mechanism != NULL)
2073     {
2074       void *args[]  = { pamh, session };
2075 
2076       /*
2077        * XXX this API is broken - how can we extract the password policy
2078        * controls? do we need to implement DIGEST-MD5 ourself?
2079        */
2080       rc = ldap_sasl_interactive_bind_s (session->ld, session->info->userdn,
2081 					 session->conf->sasl_mechanism,
2082 					 psrvctrls, NULL,
2083 #ifdef LDAP_SASL_AUTOMATIC
2084 					 LDAP_SASL_AUTOMATIC,
2085 #else
2086 					 0,
2087 #endif
2088 					 _do_sasl_interact, &args);
2089       if (rc != LDAP_SUCCESS)
2090 	{
2091           int ld_errno = ldap_get_lderrno (session->ld, 0, 0);
2092 
2093 	  syslog (LOG_ERR, "pam_ldap: ldap_sasl_interactive_bind %s",
2094 		  ldap_err2string (ld_errno));
2095           if (ld_errno == LDAP_SERVER_DOWN && !reconnect)
2096             {
2097               reconnect = 1;
2098               goto retry;
2099             }
2100 	  _pam_overwrite (session->info->userpw);
2101 	  _pam_drop (session->info->userpw);
2102 	  return PAM_AUTHINFO_UNAVAIL;
2103 	}
2104       session->info->bound_as_user = 1;
2105       return PAM_SUCCESS;
2106     }
2107 #endif
2108 #if defined(HAVE_LDAP_SASL_BIND) && defined(LDAP_SASL_SIMPLE)
2109   if (session->conf->version > LDAP_VERSION2)
2110     {
2111       userpw.bv_val = session->info->userpw;
2112       userpw.bv_len = (userpw.bv_val != 0) ? strlen (userpw.bv_val) : 0;
2113 
2114       rc =
2115 	ldap_sasl_bind (session->ld, session->info->userdn, LDAP_SASL_SIMPLE,
2116 			&userpw, psrvctrls, 0, &msgid);
2117       if (rc != LDAP_SUCCESS || msgid == -1)
2118 	{
2119           int ld_errno = ldap_get_lderrno (session->ld, 0, 0);
2120 
2121 	  syslog (LOG_ERR, "pam_ldap: ldap_sasl_bind %s",
2122 		  ldap_err2string (ld_errno));
2123           if (ld_errno == LDAP_SERVER_DOWN && !reconnect)
2124             {
2125               reconnect = 1;
2126               goto retry;
2127             }
2128 	  _pam_overwrite (session->info->userpw);
2129 	  _pam_drop (session->info->userpw);
2130 	  return PAM_AUTHINFO_UNAVAIL;
2131 	}
2132     }
2133   else
2134     {
2135       msgid = ldap_simple_bind (session->ld, session->info->userdn,
2136 				session->info->userpw);
2137       if (msgid == -1)
2138 	{
2139           int ld_errno = ldap_get_lderrno (session->ld, 0, 0);
2140 
2141 	  syslog (LOG_ERR, "pam_ldap: ldap_simple_bind %s",
2142 		  ldap_err2string (ld_errno));
2143           if (ld_errno == LDAP_SERVER_DOWN && !reconnect)
2144             {
2145               reconnect = 1;
2146               goto retry;
2147             }
2148 	  _pam_overwrite (session->info->userpw);
2149 	  _pam_drop (session->info->userpw);
2150 	  return PAM_AUTHINFO_UNAVAIL;
2151 	}
2152     }
2153 #else
2154   msgid =
2155     ldap_simple_bind (session->ld, session->info->userdn,
2156 		      session->info->userpw);
2157   if (msgid == -1)
2158     {
2159       int ld_errno = ldap_get_lderrno (session->ld, 0, 0);
2160 
2161 
2162 
2163 
2164       syslog (LOG_ERR, "pam_ldap: ldap_simple_bind %s",
2165 	      ldap_err2string (ld_errno));
2166       if (ld_errno == LDAP_SERVER_DOWN && !reconnect)
2167         {
2168           reconnect = 1;
2169           goto retry;
2170         }
2171       _pam_overwrite (session->info->userpw);
2172       _pam_drop (session->info->userpw);
2173       return PAM_AUTHINFO_UNAVAIL;
2174     }
2175 #endif /* HAVE_LDAP_SASL_BIND && LDAP_SASL_SIMPLE */
2176 
2177   timeout.tv_sec = 10;
2178   timeout.tv_usec = 0;
2179   rc = ldap_result (session->ld, msgid, FALSE, &timeout, &result);
2180   if (rc == -1 || rc == 0)
2181     {
2182       int ld_errno = ldap_get_lderrno (session->ld, 0, 0);
2183 
2184       syslog (LOG_ERR, "pam_ldap: ldap_result %s",
2185 	      ldap_err2string (ld_errno));
2186       if (ld_errno == LDAP_SERVER_DOWN && !reconnect)
2187         {
2188           reconnect = 1;
2189           goto retry;
2190         }
2191       _pam_overwrite (session->info->userpw);
2192       _pam_drop (session->info->userpw);
2193       return PAM_AUTHINFO_UNAVAIL;
2194     }
2195 
2196 #if defined(HAVE_LDAP_PARSE_RESULT) && defined(HAVE_LDAP_CONTROLS_FREE)
2197   controls = 0;
2198   parserc =
2199     ldap_parse_result (session->ld, result, &rc, 0, 0, 0, &controls, TRUE);
2200   if (parserc != LDAP_SUCCESS)
2201     {
2202       syslog (LOG_ERR, "pam_ldap: ldap_parse_result %s",
2203 	      ldap_err2string (parserc));
2204       _pam_overwrite (session->info->userpw);
2205       _pam_drop (session->info->userpw);
2206       return PAM_SERVICE_ERR;
2207     }
2208   if (controls != NULL)
2209     {
2210       LDAPControl **ctlp;
2211       for (ctlp = controls; *ctlp != NULL; ctlp++)
2212 	{
2213 	  if (!strcmp ((*ctlp)->ldctl_oid, LDAP_CONTROL_PWEXPIRING))
2214 	    {
2215 	      char seconds[32];
2216 	      snprintf (seconds, sizeof seconds, "%.*s",
2217 			(int) (*ctlp)->ldctl_value.bv_len,
2218 			(*ctlp)->ldctl_value.bv_val);
2219 	      session->info->password_expiration_time = atol (seconds);
2220 	    }
2221 	  else if (!strcmp ((*ctlp)->ldctl_oid, LDAP_CONTROL_PWEXPIRED))
2222 	    {
2223 	      if (session->info->policy_error == POLICY_ERROR_SUCCESS)
2224 		session->info->policy_error = POLICY_ERROR_PASSWORD_EXPIRED;
2225 	      rc = LDAP_SUCCESS;
2226 	      /* That may be a lie, but we need to get to the acct_mgmt
2227 	       * step and force the change...
2228 	       */
2229 	    }
2230 	  else if (!strcmp ((*ctlp)->ldctl_oid, LDAP_CONTROL_PASSWORDPOLICYRESPONSE))
2231 	    {
2232 	      int rc2;
2233 
2234 	      rc2 = _get_password_policy_response_value (&(*ctlp)->ldctl_value,
2235 							 session);
2236 
2237 	      if (rc2 != LDAP_SUCCESS)
2238 		{
2239 		  /*
2240 		   * If decoding policy control failed, and we're not already
2241 		   * planning to report an error, return the decoding error.
2242 		   */
2243 	          if (rc == LDAP_SUCCESS)
2244 		    {
2245                       rc = rc2;
2246 		    }
2247 		}
2248 	      else
2249 		{
2250 		  /*
2251 		   * If we have a policy error, and it's one which the PAM spec
2252 		   * expects us to communicate via the acct_mgmt callback,
2253 		   * then we suppress the error.  If it's a different kind of
2254 		   * policy error, then make sure we indicate the error now.
2255 		   */
2256 		  switch (session->info->policy_error)
2257 		    {
2258 		    case POLICY_ERROR_SUCCESS:
2259 		      break;
2260 		    case POLICY_ERROR_CHANGE_AFTER_RESET:
2261 		    case POLICY_ERROR_PASSWORD_EXPIRED:
2262 		      rc = LDAP_SUCCESS;
2263 		      break;
2264 		    case POLICY_ERROR_ACCOUNT_LOCKED:
2265 		    case POLICY_ERROR_PASSWORD_MOD_NOT_ALLOWED:
2266 		    case POLICY_ERROR_MUST_SUPPLY_OLD_PASSWORD:
2267 		    case POLICY_ERROR_INSUFFICIENT_PASSWORD_QUALITY:
2268 		    case POLICY_ERROR_PASSWORD_TOO_SHORT:
2269 		    case POLICY_ERROR_PASSWORD_TOO_YOUNG:
2270 		    case POLICY_ERROR_PASSWORD_INSUFFICIENT:
2271 		    default:
2272 		      ldap_controls_free (controls);
2273 		      _pam_overwrite (session->info->userpw);
2274 		      _pam_drop (session->info->userpw);
2275 		      return PAM_AUTH_ERR;
2276 		      break;
2277 		    }
2278 		}
2279 	    }
2280 	}
2281       ldap_controls_free (controls);
2282     }
2283 #else
2284   rc = ldap_result2error (session->ld, result, TRUE);
2285 #endif
2286 
2287   if (rc != LDAP_SUCCESS)
2288     {
2289       syslog (LOG_ERR, "pam_ldap: error trying to bind as user \"%s\" (%s)",
2290 	      session->info->userdn, ldap_err2string (rc));
2291       _pam_overwrite (session->info->userpw);
2292       _pam_drop (session->info->userpw);
2293       return PAM_AUTH_ERR;
2294     }
2295 
2296   /* check if we need to unset userpw */
2297   switch (session->info->policy_error)
2298     {
2299     case POLICY_ERROR_SUCCESS:
2300     case POLICY_ERROR_PASSWORD_EXPIRED:
2301     case POLICY_ERROR_CHANGE_AFTER_RESET:
2302      break;
2303     default:
2304       _pam_overwrite (session->info->userpw);
2305       _pam_drop (session->info->userpw);
2306       break;
2307     }
2308   /* else userpw is now set. Be sure to clobber it later. */
2309 
2310   session->info->bound_as_user = 1;
2311 
2312   return PAM_SUCCESS;
2313 }
2314 
2315 static int
_get_integer_value(LDAP * ld,LDAPMessage * e,const char * attr,int * ptr)2316 _get_integer_value (LDAP * ld, LDAPMessage * e, const char *attr, int *ptr)
2317 {
2318   char **vals;
2319 
2320   vals = ldap_get_values (ld, e, (char *) attr);
2321   if (vals == NULL)
2322     {
2323       return PAM_AUTHINFO_UNAVAIL;
2324     }
2325   *ptr = atoi (vals[0]);
2326   ldap_value_free (vals);
2327 
2328   return PAM_SUCCESS;
2329 }
2330 
2331 static int
_get_long_integer_value(LDAP * ld,LDAPMessage * e,const char * attr,long int * ptr)2332 _get_long_integer_value (LDAP * ld, LDAPMessage * e, const char *attr,
2333 			 long int *ptr)
2334 {
2335   char **vals;
2336 
2337   vals = ldap_get_values (ld, e, (char *) attr);
2338   if (vals == NULL)
2339     {
2340       return PAM_AUTHINFO_UNAVAIL;
2341     }
2342   *ptr = atol (vals[0]);
2343   ldap_value_free (vals);
2344 
2345   return PAM_SUCCESS;
2346 }
2347 
2348 
2349 #ifdef notdef
2350 static int
_oc_check(LDAP * ld,LDAPMessage * e,const char * oc)2351 _oc_check (LDAP * ld, LDAPMessage * e, const char *oc)
2352 {
2353   char **vals, **p;
2354   int rc = 0;
2355 
2356   vals = ldap_get_values (ld, e, "objectClass");
2357   if (vals == NULL)
2358     {
2359       return PAM_AUTHINFO_UNAVAIL;
2360     }
2361 
2362   for (p = vals; *p != NULL; p++)
2363     {
2364       if (!strcasecmp (*p, oc))
2365 	{
2366 	  rc = 1;
2367 	  break;
2368 	}
2369     }
2370 
2371   ldap_value_free (vals);
2372 
2373   return rc;
2374 }
2375 #endif /* notdef */
2376 
2377 static int
_get_string_value(LDAP * ld,LDAPMessage * e,const char * attr,char ** ptr)2378 _get_string_value (LDAP * ld, LDAPMessage * e, const char *attr, char **ptr)
2379 {
2380   char **vals;
2381   int rc;
2382 
2383   vals = ldap_get_values (ld, e, (char *) attr);
2384   if (vals == NULL)
2385     {
2386       return PAM_AUTHINFO_UNAVAIL;
2387     }
2388   *ptr = strdup (vals[0]);
2389   if (*ptr == NULL)
2390     {
2391       rc = PAM_BUF_ERR;
2392     }
2393   else
2394     {
2395       rc = PAM_SUCCESS;
2396     }
2397 
2398   ldap_value_free (vals);
2399 
2400   return rc;
2401 }
2402 
2403 static int
_get_string_values(LDAP * ld,LDAPMessage * e,const char * attr,char *** ptr)2404 _get_string_values (LDAP * ld, LDAPMessage * e, const char *attr, char ***ptr)
2405 {
2406   char **vals;
2407 
2408   vals = ldap_get_values (ld, e, (char *) attr);
2409   if (vals == NULL)
2410     {
2411       return PAM_AUTHINFO_UNAVAIL;
2412     }
2413   *ptr = vals;
2414 
2415   return PAM_SUCCESS;
2416 }
2417 
2418 static int
_has_deny_value(char ** src,const char * tgt)2419 _has_deny_value (char **src, const char *tgt)
2420 {
2421 
2422   char **p;
2423 
2424   for (p = src; *p != NULL; p++)
2425     {
2426       if (**p == '!' && !strcasecmp (*p + 1, tgt))
2427 	{
2428 	  return 1;
2429 	}
2430     }
2431 
2432   return 0;
2433 }
2434 
2435 static int
_has_value(char ** src,const char * tgt)2436 _has_value (char **src, const char *tgt)
2437 {
2438   char **p;
2439 
2440   for (p = src; *p != NULL; p++)
2441     {
2442       if (!strcasecmp (*p, tgt))
2443 	{
2444 	  return 1;
2445 	}
2446     }
2447 
2448   return 0;
2449 }
2450 
2451 static int
_service_ok(pam_handle_t * pamh,pam_ldap_session_t * session)2452 _service_ok (pam_handle_t * pamh, pam_ldap_session_t * session)
2453 {
2454   int rc;
2455   char *service = NULL;
2456 
2457   /* simple host based access authorization */
2458   if (session->info->services_allow == NULL)
2459     {
2460       return PAM_PERM_DENIED;
2461     }
2462 
2463   rc = pam_get_item (pamh, PAM_SERVICE, (CONST_ARG void **) &service);
2464   if (rc != PAM_SUCCESS)
2465     {
2466       service = NULL;
2467     }
2468 
2469   if (service != NULL)
2470     {
2471       if (_has_deny_value (session->info->services_allow, service))
2472 	return PAM_PERM_DENIED;
2473       else if (_has_value (session->info->services_allow, service))
2474 	return PAM_SUCCESS;
2475     }
2476 
2477   /* allow wild-card entries */
2478   return (_has_value (session->info->services_allow, "*")) ? PAM_SUCCESS :
2479     PAM_PERM_DENIED;
2480 }
2481 
2482 static int
_host_ok(pam_ldap_session_t * session)2483 _host_ok (pam_ldap_session_t * session)
2484 {
2485   char hostname[MAXHOSTNAMELEN];
2486   struct hostent *h;
2487 #ifdef HAVE_GETHOSTBYNAME_R
2488   struct hostent hbuf;
2489 #if GETHOSTBYNAME_R_ARGS == 3
2490   struct hostent_data buf;
2491 #else
2492   int herr;
2493   char buf[1024];
2494 #endif /* GETHOSTBYNAME_R_ARGS == 3 */
2495 #endif /* HAVE_GETHOSTBYNAME_R */
2496   char **q;
2497 
2498   /* simple host based access authorization */
2499   if (session->info->hosts_allow == NULL)
2500     {
2501       return PAM_PERM_DENIED;
2502     }
2503 
2504 
2505   if (gethostname (hostname, sizeof hostname) < 0)
2506     {
2507       return PAM_SYSTEM_ERR;
2508     }
2509 
2510 #if defined(HAVE_GETHOSTBYNAME_R)
2511 #if GETHOSTBYNAME_R_ARGS == 6
2512   if (gethostbyname_r (hostname, &hbuf, buf, sizeof buf, &h, &herr) != 0)
2513     {
2514       return PAM_SYSTEM_ERR;
2515     }
2516 #elif GETHOSTBYNAME_R_ARGS == 5
2517   h = gethostbyname_r (hostname, &hbuf, buf, sizeof buf, &herr);
2518   if (h == NULL)
2519     {
2520       return PAM_SYSTEM_ERR;
2521     }
2522 #elif GETHOSTBYNAME_R_ARGS == 3
2523   if (gethostbyname_r (hostname, &hbuf, &buf) != 0)
2524     {
2525       return PAM_SYSTEM_ERR;
2526     }
2527   h = &hbuf;
2528 #else
2529 #error Unknown gethostbyname_r() implementation
2530 #endif
2531 #else
2532   h = gethostbyname (hostname);
2533   if (h == NULL)
2534     {
2535       return PAM_SYSTEM_ERR;
2536     }
2537 #endif
2538 
2539   if (h == NULL || h->h_name == NULL)
2540     return PAM_SYSTEM_ERR;
2541 
2542   if (_has_deny_value (session->info->hosts_allow, h->h_name))
2543     return PAM_PERM_DENIED;
2544   else if (_has_value (session->info->hosts_allow, h->h_name))
2545     return PAM_SUCCESS;
2546 
2547   if (h->h_aliases != NULL)
2548     {
2549       for (q = h->h_aliases; *q != NULL; q++)
2550 	{
2551 	  if (_has_value (session->info->hosts_allow, *q))
2552 	    return PAM_SUCCESS;
2553 	  if (_has_deny_value (session->info->hosts_allow, *q))
2554 	    return PAM_PERM_DENIED;
2555 	}
2556     }
2557 
2558   /* allow wild-card entries */
2559   if (_has_value (session->info->hosts_allow, "*"))
2560     {
2561       return PAM_SUCCESS;
2562     }
2563 
2564   return PAM_PERM_DENIED;
2565 }
2566 
2567 static char *
_get_md5_salt(char saltbuf[16])2568 _get_md5_salt (char saltbuf[16])
2569 {
2570   md5_state_t state;
2571   md5_byte_t digest[16];
2572   struct timeval tv;
2573   int i;
2574 
2575   _pam_ldap_md5_init (&state);
2576   gettimeofday (&tv, NULL);
2577   _pam_ldap_md5_append (&state, (unsigned char *) &tv, sizeof (tv));
2578   i = getpid ();
2579   _pam_ldap_md5_append (&state, (unsigned char *) &i, sizeof (i));
2580   i = clock ();
2581   _pam_ldap_md5_append (&state, (unsigned char *) &i, sizeof (i));
2582   _pam_ldap_md5_append (&state, (unsigned char *) saltbuf, sizeof (saltbuf));
2583   _pam_ldap_md5_finish (&state, digest);
2584 
2585   strcpy (saltbuf, "$1$");
2586   for (i = 0; i < 8; i++)
2587     saltbuf[i + 3] = i64c (digest[i] & 0x3f);
2588 
2589   saltbuf[i + 3] = '\0';
2590 
2591   return saltbuf;
2592 }
2593 
2594 static char *
_get_salt(char salt[16])2595 _get_salt (char salt[16])
2596 {
2597   int i;
2598   int j;
2599 
2600   srand (time (NULL));
2601 
2602   for (j = 0; j < 2; j++)
2603     {
2604       i = rand () % 3;
2605       switch (i)
2606 	{
2607 	case 0:
2608 	  i = (rand () % (57 - 46)) + 46;
2609 	  break;
2610 	case 1:
2611 	  i = (rand () % (90 - 65)) + 65;
2612 	  break;
2613 	case 2:
2614 	  i = (rand () % (122 - 97)) + 97;
2615 	  break;
2616 	}
2617       salt[j] = i;
2618     }
2619   salt[2] = '\0';
2620   return salt;
2621 }
2622 
2623 static int
_escape_string(const char * str,char * buf,size_t buflen)2624 _escape_string (const char *str, char *buf, size_t buflen)
2625 {
2626   int ret = PAM_BUF_ERR;
2627   char *p = buf;
2628   char *limit = p + buflen - 3;
2629   const char *s = str;
2630 
2631   while (p < limit && *s)
2632     {
2633       switch (*s)
2634 	{
2635 	case '*':
2636 	  strcpy (p, "\\2a");
2637 	  p += 3;
2638 	  break;
2639 	case '(':
2640 	  strcpy (p, "\\28");
2641 	  p += 3;
2642 	  break;
2643 	case ')':
2644 	  strcpy (p, "\\29");
2645 	  p += 3;
2646 	  break;
2647 	case '\\':
2648 	  strcpy (p, "\\5c");
2649 	  p += 3;
2650 	  break;
2651 	default:
2652 	  *p++ = *s;
2653 	  break;
2654 	}
2655       s++;
2656     }
2657 
2658   if (*s == '\0')
2659     {
2660       /* got to end */
2661       *p = '\0';
2662       ret = PAM_SUCCESS;
2663     }
2664 
2665   return ret;
2666 }
2667 
2668 static char *_pam_ldap_attrs[] = {
2669   "host",
2670   "authorizedService",
2671   "shadowExpire",
2672   "shadowFlag",
2673   "shadowInactive",
2674   "shadowLastChange",
2675   "shadowMax",
2676   "shadowMin",
2677   "shadowWarning",
2678   "uidNumber",
2679   NULL
2680 };
2681 
2682 static int
_get_user_info(pam_ldap_session_t * session,const char * user)2683 _get_user_info (pam_ldap_session_t * session, const char *user)
2684 {
2685   char filter[LDAP_FILT_MAXSIZ], escapedUser[LDAP_FILT_MAXSIZ];
2686   int rc;
2687   LDAPMessage *res, *msg;
2688   pam_ssd_t *ssd, ssdummy;
2689 
2690   rc = _connect_anonymously (session);
2691   if (rc != PAM_SUCCESS)
2692     return rc;
2693 
2694 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_SIZELIMIT)
2695   rc = 1;
2696   (void) ldap_set_option (session->ld, LDAP_OPT_SIZELIMIT, &rc);
2697 #else
2698   session->ld->ld_sizelimit = 1;
2699 #endif
2700 
2701   rc = _escape_string (user, escapedUser, sizeof (escapedUser));
2702   if (rc != PAM_SUCCESS)
2703     {
2704       return rc;
2705     }
2706 
2707   ssd = session->conf->ssd;
2708   if (ssd == NULL)
2709     {
2710       ssd = &ssdummy;
2711       ssd->filter = session->conf->filter;
2712       ssd->base = session->conf->base;
2713       ssd->scope = session->conf->scope;
2714       ssd->next = NULL;
2715     }
2716 nxt:
2717   if (session->conf->filter != NULL && ssd->filter != NULL)
2718     {
2719       snprintf (filter, sizeof filter, "(&(%s)(%s)(%s=%s))",
2720 		ssd->filter, session->conf->filter, session->conf->userattr,
2721 		escapedUser);
2722     }
2723   else if (ssd->filter != NULL)
2724     {
2725       snprintf (filter, sizeof filter, "(&(%s)(%s=%s))",
2726 		ssd->filter, session->conf->userattr, escapedUser);
2727     }
2728   else if (session->conf->filter != NULL)
2729     {
2730       snprintf (filter, sizeof filter, "(&(%s)(%s=%s))",
2731 		session->conf->filter, session->conf->userattr, escapedUser);
2732     }
2733   else
2734     {
2735       snprintf (filter, sizeof filter, "(%s=%s)",
2736 		session->conf->userattr, escapedUser);
2737     }
2738 
2739   rc = ldap_search_s (session->ld, ssd->base, ssd->scope,
2740 		      filter, _pam_ldap_attrs, 0, &res);
2741 
2742   if (rc != LDAP_SUCCESS &&
2743       rc != LDAP_TIMELIMIT_EXCEEDED && rc != LDAP_SIZELIMIT_EXCEEDED)
2744     {
2745       syslog (LOG_ERR, "pam_ldap: ldap_search_s %s", ldap_err2string (rc));
2746       return PAM_USER_UNKNOWN;
2747     }
2748 
2749   msg = ldap_first_entry (session->ld, res);
2750   if (msg == NULL)
2751     {
2752       ldap_msgfree (res);
2753       if (ssd->next)
2754 	{
2755 	  ssd = ssd->next;
2756 	  goto nxt;
2757 	}
2758       return PAM_USER_UNKNOWN;
2759     }
2760 
2761   if (session->info != NULL)
2762     {
2763       _release_user_info (&session->info);
2764     }
2765 
2766   session->info =
2767     (pam_ldap_user_info_t *) calloc (1, sizeof (pam_ldap_user_info_t));
2768   if (session->info == NULL)
2769     {
2770       ldap_msgfree (res);
2771       return PAM_BUF_ERR;
2772     }
2773 
2774   rc = _get_string_value (session->ld, msg, session->conf->userattr,
2775                     &session->info->username);
2776   if (rc != PAM_SUCCESS)
2777     {
2778       session->info->username = strdup (user);
2779     }
2780 
2781   if (session->info->username == NULL)
2782     {
2783       ldap_msgfree (res);
2784       _release_user_info (&session->info);
2785       return PAM_BUF_ERR;
2786     }
2787 
2788   session->info->userdn = ldap_get_dn (session->ld, msg);
2789   if (session->info->userdn == NULL)
2790     {
2791       ldap_msgfree (res);
2792       _release_user_info (&session->info);
2793       return PAM_SERVICE_ERR;
2794     }
2795 
2796   session->info->bound_as_user = 0;
2797   session->info->policy_error = POLICY_ERROR_SUCCESS;
2798 
2799   /*
2800    * it might be better to do a compare later, that way we can
2801    * avoid fetching any attributes at all
2802    */
2803   _get_string_values (session->ld, msg, "host", &session->info->hosts_allow);
2804   _get_string_values (session->ld, msg, "authorizedService",
2805 		      &session->info->services_allow);
2806 
2807   /* get UID */
2808 #ifdef UID_NOBODY
2809   session->info->uid = UID_NOBODY;
2810 #else
2811   session->info->uid = (uid_t) - 2;
2812 #endif /* UID_NOBODY */
2813   _get_integer_value (session->ld, msg, "uidNumber",
2814 		      (int *) &session->info->uid);
2815 
2816   /*
2817    * get mapped user; some PAM host applications let PAM_USER be reset
2818    * by the user (such as some of those provided with FreeBSD).
2819    */
2820   session->info->tmpluser = NULL;
2821   if (session->conf->tmplattr != NULL)
2822     {
2823       if (_get_string_value (session->ld,
2824 			     msg,
2825 			     session->conf->tmplattr,
2826 			     &session->info->tmpluser) != PAM_SUCCESS)
2827 	{
2828 	  /* set to default template user */
2829 	  session->info->tmpluser =
2830 	    session->conf->tmpluser ? strdup (session->conf->tmpluser) : NULL;
2831 	}
2832     }
2833 
2834   /* Assume shadow controls.  Allocate shadow structure and link to session. */
2835   session->info->shadow.lstchg = -1;
2836   session->info->shadow.min = 0;
2837   session->info->shadow.max = 0;
2838   session->info->shadow.warn = 0;
2839   session->info->shadow.inact = 0;
2840   session->info->shadow.expire = 0;
2841   session->info->shadow.flag = 0;
2842 
2843   _get_long_integer_value (session->ld, msg, "shadowLastChange",
2844 			   &session->info->shadow.lstchg);
2845   _get_long_integer_value (session->ld, msg, "shadowMin",
2846 			   &session->info->shadow.min);
2847   _get_long_integer_value (session->ld, msg, "shadowMax",
2848 			   &session->info->shadow.max);
2849   _get_long_integer_value (session->ld, msg, "shadowWarning",
2850 			   &session->info->shadow.warn);
2851   _get_long_integer_value (session->ld, msg, "shadowInactive",
2852 			   &session->info->shadow.inact);
2853   _get_long_integer_value (session->ld, msg, "shadowExpire",
2854 			   &session->info->shadow.expire);
2855   _get_long_integer_value (session->ld, msg, "shadowFlag",
2856 			   &session->info->shadow.flag);
2857 
2858   ldap_msgfree (res);
2859 
2860   return PAM_SUCCESS;
2861 }
2862 
2863 static int
_pam_ldap_get_session(pam_handle_t * pamh,const char * username,const char * configFile,pam_ldap_session_t ** psession)2864 _pam_ldap_get_session (pam_handle_t * pamh, const char *username,
2865 		       const char *configFile, pam_ldap_session_t ** psession)
2866 {
2867   pam_ldap_session_t *session;
2868   int rc;
2869 
2870   if (pam_get_data
2871       (pamh, PADL_LDAP_SESSION_DATA, (const void **) &session) == PAM_SUCCESS)
2872     {
2873       /*
2874        * we cache the information retrieved from the LDAP server, however
2875        * we need to flush this if the application has changed the user
2876        * or configuration file.
2877        *
2878        * For template users, note that pam_ldap may _RESET_ the username!
2879        */
2880       if (session->info != NULL &&
2881 	  (strcmp (username, session->info->username) != 0))
2882 	{
2883 	  _release_user_info (&session->info);
2884 	}
2885 
2886       if (configFile == NULL)
2887 	{
2888 	  /* Default configuration file requested. */
2889 	  if (session->conf->configFile != NULL)
2890 	    _release_user_info (&session->info);
2891 	}
2892       else
2893 	{
2894 	  /* Non-default configuration file requested. */
2895 	  if (session->conf->configFile == NULL ||
2896 	      (strcmp (configFile, session->conf->configFile) != 0))
2897 	    {
2898 	      _release_user_info (&session->info);
2899 	    }
2900 	}
2901 
2902       *psession = session;
2903 #if LDAP_SET_REBIND_PROC_ARGS < 3
2904       global_session = *psession;
2905 #endif
2906       return PAM_SUCCESS;
2907     }
2908 
2909   *psession = NULL;
2910 
2911   session = (pam_ldap_session_t *) calloc (1, sizeof (*session));
2912 #if LDAP_SET_REBIND_PROC_ARGS < 3
2913   global_session = session;
2914 #endif
2915   if (session == NULL)
2916     {
2917       return PAM_BUF_ERR;
2918     }
2919 
2920   session->ld = NULL;
2921   session->conf = NULL;
2922   session->info = NULL;
2923 
2924 #ifdef YPLDAPD
2925   rc = _ypldapd_read_config (&session->conf);
2926   if (rc != PAM_SUCCESS)
2927     {
2928       _release_config (&session->conf);
2929 #endif /* YPLDAPD */
2930       rc = _read_config (configFile, &session->conf);
2931       if (rc != PAM_SUCCESS)
2932 	{
2933 	  _release_config (&session->conf);
2934 	  free (session);
2935 	  return rc;
2936 	}
2937 #ifdef YPLDAPD
2938     }
2939 #endif /* YPLDAPD */
2940 
2941   rc =
2942     pam_set_data (pamh, PADL_LDAP_SESSION_DATA, (void *) session,
2943 		  _pam_ldap_cleanup_session);
2944   if (rc != PAM_SUCCESS)
2945     {
2946       _release_config (&session->conf);
2947       free (session);
2948       return rc;
2949     }
2950 
2951   *psession = session;
2952 
2953   return PAM_SUCCESS;
2954 }
2955 
2956 static int
_session_reopen(pam_ldap_session_t * session)2957 _session_reopen (pam_ldap_session_t * session)
2958 {
2959   /* FYI: V3 lets us avoid five unneeded binds in a password change */
2960   if (session->conf->version == LDAP_VERSION2)
2961     {
2962       if (session->ld != NULL)
2963 	{
2964 	  ldap_unbind (session->ld);
2965 	  session->ld = NULL;
2966 	}
2967       if (session->info != NULL)
2968 	{
2969 	  session->info->bound_as_user = 0;
2970 	}
2971       return _open_session (session);
2972     }
2973   return PAM_SUCCESS;
2974 }
2975 
2976 static int
_get_password_policy(pam_ldap_session_t * session,pam_ldap_password_policy_t * policy)2977 _get_password_policy (pam_ldap_session_t * session,
2978 		      pam_ldap_password_policy_t * policy)
2979 {
2980   int rc = PAM_SUCCESS;
2981   LDAPMessage *res, *msg;
2982 
2983   /* set some reasonable defaults */
2984   memset (policy, 0, sizeof (*policy));
2985   policy->password_min_length = 6;
2986   policy->password_max_failure = 3;
2987 
2988   if (session->conf->getpolicy == 0)
2989     {
2990       return PAM_SUCCESS;
2991     }
2992 
2993   rc = _connect_anonymously (session);
2994   if (rc != PAM_SUCCESS)
2995     {
2996       return rc;
2997     }
2998 
2999 #if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_SIZELIMIT)
3000   rc = 1;
3001   (void) ldap_set_option (session->ld, LDAP_OPT_SIZELIMIT, &rc);
3002 #else
3003   session->ld->ld_sizelimit = 1;
3004 #endif /* LDAP_VERSION3_API */
3005 
3006   rc = ldap_search_s (session->ld,
3007 		      "",
3008 		      LDAP_SCOPE_BASE,
3009 		      "(objectclass=passwordPolicy)", NULL, 0, &res);
3010 
3011   if (rc == LDAP_SUCCESS ||
3012       rc == LDAP_TIMELIMIT_EXCEEDED || rc == LDAP_SIZELIMIT_EXCEEDED)
3013     {
3014       msg = ldap_first_entry (session->ld, res);
3015       if (msg != NULL)
3016 	{
3017 	  _get_integer_value (session->ld, msg, "passwordMaxFailure",
3018 			      &policy->password_max_failure);
3019 	  _get_integer_value (session->ld, msg, "passwordMinLength",
3020 			      &policy->password_min_length);
3021 	}
3022       ldap_msgfree (res);
3023     }
3024 
3025   return PAM_SUCCESS;
3026 }
3027 
3028 static int
_do_authentication(pam_handle_t * pamh,pam_ldap_session_t * session,const char * user,const char * password)3029 _do_authentication (pam_handle_t *pamh,
3030 		    pam_ldap_session_t * session,
3031 		    const char *user, const char *password)
3032 {
3033   int rc = PAM_SUCCESS;
3034 
3035   if (session->info == NULL)
3036     {
3037       rc = _get_user_info (session, user);
3038       if (rc != PAM_SUCCESS)
3039 	return rc;
3040     }
3041 
3042   rc = _session_reopen (session);
3043   if (rc != PAM_SUCCESS)
3044     return rc;
3045 
3046   rc = _connect_as_user (pamh, session, password);
3047   _session_reopen (session);
3048   _connect_anonymously (session);
3049   return rc;
3050 }
3051 
3052 static int
_update_authtok(pam_handle_t * pamh,pam_ldap_session_t * session,const char * user,const char * old_password,const char * new_password)3053 _update_authtok (pam_handle_t *pamh,
3054 		 pam_ldap_session_t * session,
3055 		 const char *user,
3056 		 const char *old_password, const char *new_password)
3057 {
3058   char *strvalsold[2];
3059   char *strvalsnew[2];
3060   LDAPMod mod, mod2;
3061   LDAPMod *mods[3];
3062   char buf[64], saltbuf[16];
3063   int rc = PAM_SUCCESS;
3064   size_t i;
3065 
3066   /* for Active Directory */
3067 
3068   struct berval bvalold;
3069   struct berval bvalnew;
3070   struct berval *bvalsold[2];
3071   struct berval *bvalsnew[2];
3072   char old_password_with_quotes[17], new_password_with_quotes[17];
3073   char old_unicode_password[34], new_unicode_password[34];
3074 
3075 #ifdef LDAP_EXOP_MODIFY_PASSWD
3076   /* for OpenLDAP password change extended operation */
3077   BerElement *ber;
3078   struct berval *bv;
3079   char *retoid;
3080   struct berval *retdata;
3081 #endif /* LDAP_EXOP_MODIFY_PASSWD */
3082 
3083   if (session->info == NULL)
3084     {
3085       rc = _get_user_info (session, user);
3086       if (rc != PAM_SUCCESS)
3087 	{
3088 	  return rc;
3089 	}
3090     }
3091 
3092   if (!session->conf->rootbinddn || geteuid () != 0)
3093     {
3094       /* We're not root or don't have a rootbinddn so
3095        * let's try binding as the user.
3096        *
3097        * FIXME:
3098        * Do we really want to do this? It allows the
3099        * system to be configured in such a way that the
3100        * user can bypass local password policy
3101        */
3102       rc = _session_reopen (session);
3103       if (rc != PAM_SUCCESS)
3104 	return rc;
3105 
3106       rc = _connect_as_user (pamh, session, old_password);
3107       if (rc != PAM_SUCCESS)
3108 	return rc;
3109     }
3110 
3111   switch (session->conf->password_type)
3112     {
3113     case PASSWORD_CLEAR:
3114       strvalsnew[0] = (char *) new_password;
3115       strvalsnew[1] = NULL;
3116 
3117       mod.mod_op = LDAP_MOD_REPLACE;
3118       mod.mod_type = (char *) "userPassword";
3119       mod.mod_values = strvalsnew;
3120 
3121       mods[0] = &mod;
3122       mods[1] = NULL;
3123 
3124       break;
3125 
3126     case PASSWORD_CRYPT:
3127       _get_salt (saltbuf);
3128       snprintf (buf, sizeof buf, "{crypt}%s", crypt (new_password, saltbuf));
3129       strvalsnew[0] = buf;
3130       strvalsnew[1] = NULL;
3131 
3132       mod.mod_op = LDAP_MOD_REPLACE;
3133       mod.mod_type = (char *) "userPassword";
3134       mod.mod_values = strvalsnew;
3135 
3136       mods[0] = &mod;
3137       mods[1] = NULL;
3138 
3139       break;
3140 
3141     case PASSWORD_MD5:
3142       _get_md5_salt (saltbuf);
3143       snprintf (buf, sizeof buf, "{crypt}%s", crypt (new_password, saltbuf));
3144       strvalsnew[0] = buf;
3145       strvalsnew[1] = NULL;
3146 
3147       mod.mod_op = LDAP_MOD_REPLACE;
3148       mod.mod_type = (char *) "userPassword";
3149       mod.mod_values = strvalsnew;
3150 
3151       mods[0] = &mod;
3152       mods[1] = NULL;
3153 
3154       break;
3155 
3156     case PASSWORD_CLEAR_REMOVE_OLD:
3157       /* NDSrequires that the old password is first removed */
3158       strvalsold[0] = (char *) old_password;
3159       strvalsold[1] = NULL;
3160       strvalsnew[0] = (char *) new_password;
3161       strvalsnew[1] = NULL;
3162 
3163       mod.mod_vals.modv_strvals = strvalsold;
3164       mod.mod_type = (char *) "userPassword";
3165       mod.mod_op = LDAP_MOD_DELETE;
3166 
3167       mod2.mod_vals.modv_strvals = strvalsnew;
3168       mod2.mod_type = (char *) "userPassword";
3169       mod2.mod_op = LDAP_MOD_ADD;
3170 
3171       mods[0] = &mod;
3172       mods[1] = &mod2;
3173       mods[2] = NULL;
3174 
3175       break;
3176 
3177     case PASSWORD_AD:
3178       /*
3179        * Patch from Norbert Klasen <klasen@zdv.uni-tuebingen.de>:
3180        *
3181        * To be able to change a password in AD via LDAP, an SSL connection
3182        * with a cipher strength of at least 128 bit must be established.
3183        * http://support.microsoft.com/support/kb/articles/q264/4/80.ASP
3184        * http://support.microsoft.com/support/kb/articles/Q247/0/78.ASP
3185        *
3186        * The password attribute used by AD is unicodePwd. Its syntax is octect
3187        * string. The actual value is the password surrounded by quotes in
3188        * Unicode (LSBFirst).
3189        *
3190        * NT passwords can have max. 14 characters.
3191        *
3192        * FIXME:
3193        * The conversion to Unicode only works if the locale is
3194        * ISO-8859-1 (aka Latin-1) [of which ASCII is a subset].
3195        */
3196 
3197       snprintf (new_password_with_quotes, sizeof (new_password_with_quotes),
3198 		"\"%s\"", new_password);
3199       memset (new_unicode_password, 0, sizeof (new_unicode_password));
3200       for (i = 0; i < strlen (new_password_with_quotes); i++)
3201 	new_unicode_password[i * 2] = new_password_with_quotes[i];
3202       bvalnew.bv_val = new_unicode_password;
3203       bvalnew.bv_len = strlen (new_password_with_quotes) * 2;
3204 
3205       bvalsnew[0] = &bvalnew;
3206       bvalsnew[1] = NULL;
3207       mod.mod_vals.modv_bvals = bvalsnew;
3208       mod.mod_type = (char *) "unicodePwd";
3209 
3210       if (!session->conf->rootbinddn || getuid () != 0)
3211 	{
3212 	  /* user must supply old password */
3213 	  snprintf (old_password_with_quotes,
3214 		    sizeof (old_password_with_quotes), "\"%s\"",
3215 		    old_password);
3216 	  memset (old_unicode_password, 0, sizeof (old_unicode_password));
3217 	  for (i = 0; i < strlen (old_password_with_quotes); i++)
3218 	    old_unicode_password[i * 2] = old_password_with_quotes[i];
3219 	  bvalold.bv_val = old_unicode_password;
3220 	  bvalold.bv_len = strlen (old_password_with_quotes) * 2;
3221 
3222 	  bvalsold[0] = &bvalold;
3223 	  bvalsold[1] = NULL;
3224 	  mod2.mod_vals.modv_bvals = bvalsold;
3225 	  mod2.mod_type = (char *) "unicodePwd";
3226 	  mod2.mod_op = LDAP_MOD_DELETE | LDAP_MOD_BVALUES;
3227 
3228 	  mod.mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
3229 
3230 	  mods[0] = &mod2;
3231 	  mods[1] = &mod;
3232 	  mods[2] = NULL;
3233 	}
3234       else
3235 	{
3236 	  mod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
3237 
3238 	  mods[0] = &mod;
3239 	  mods[1] = NULL;
3240 	}
3241 
3242       break;
3243 
3244     case PASSWORD_EXOP:
3245     case PASSWORD_EXOP_SEND_OLD:
3246 #ifdef LDAP_EXOP_MODIFY_PASSWD
3247       ber = ber_alloc_t (LBER_USE_DER);
3248 
3249       if (ber == NULL)
3250 	{
3251 	  return PAM_BUF_ERR;
3252 	}
3253 
3254       ber_printf (ber, "{");
3255       ber_printf (ber, "ts", LDAP_TAG_EXOP_MODIFY_PASSWD_ID,
3256 		  session->info->userdn);
3257       if (session->conf->password_type == PASSWORD_EXOP_SEND_OLD)
3258 	{
3259 	  ber_printf (ber, "ts", LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, old_password);
3260 	}
3261       ber_printf (ber, "ts", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, new_password);
3262       ber_printf (ber, "N}");
3263 
3264       rc = ber_flatten (ber, &bv);
3265       if (rc < 0)
3266 	{
3267 	  ber_free (ber, 1);
3268 	  return PAM_BUF_ERR;
3269 	}
3270 
3271       ber_free (ber, 1);
3272 
3273       rc =
3274 	ldap_extended_operation_s (session->ld, LDAP_EXOP_MODIFY_PASSWD, bv,
3275 				   NULL, NULL, &retoid, &retdata);
3276       ber_bvfree (bv);
3277 
3278       if (rc != LDAP_SUCCESS)
3279 	{
3280 	  syslog (LOG_ERR, "pam_ldap: ldap_extended_operation_s %s",
3281 		  ldap_err2string (rc));
3282 	  rc = PAM_PERM_DENIED;
3283 	}
3284       else
3285 	{
3286 	  ber_bvfree (retdata);
3287 	  ber_memfree (retoid);
3288 	  rc = PAM_SUCCESS;
3289 	}
3290 #else
3291       rc = PAM_SERVICE_ERR;
3292 #endif /* LDAP_EXOP_MODIFY_PASSWD */
3293 
3294       break;
3295     }				/* end switch */
3296 
3297   if (session->conf->password_type != PASSWORD_EXOP)
3298     {
3299       rc = ldap_modify_s (session->ld, session->info->userdn, mods);
3300       if (rc != LDAP_SUCCESS)
3301 	{
3302 	  syslog (LOG_ERR, "pam_ldap: ldap_modify_s %s",
3303 		  ldap_err2string (rc));
3304 	  rc = ldap_set_lderrno (session->ld, rc, NULL, NULL);
3305 	  if (rc != LDAP_SUCCESS)
3306 	    {
3307 	      syslog (LOG_ERR, "pam_ldap: ldap_set_lderrno %s",
3308 		      ldap_err2string (rc));
3309 	    }
3310 	  rc = PAM_PERM_DENIED;
3311 	}
3312       else
3313 	{
3314 	  rc = PAM_SUCCESS;
3315 	}
3316     }
3317 
3318   if (rc == LDAP_SUCCESS)
3319     {
3320       _pam_overwrite (session->info->userpw);
3321       _pam_drop (session->info->userpw);
3322       session->info->userpw = strdup (new_password);
3323       if (session->info->userpw == NULL)
3324 	{
3325 	  rc = PAM_BUF_ERR;
3326 	}
3327     }
3328 
3329   return rc;
3330 }
3331 
3332 static int
_get_authtok(pam_handle_t * pamh,int flags,int first)3333 _get_authtok (pam_handle_t * pamh, int flags, int first)
3334 {
3335   int rc;
3336   char *p;
3337   struct pam_message msg[1], *pmsg[1];
3338   struct pam_response *resp;
3339   struct pam_conv *conv;
3340 
3341   pmsg[0] = &msg[0];
3342   msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
3343   msg[0].msg = first ? "Password: " : "LDAP Password: ";
3344   resp = NULL;
3345 
3346   rc = pam_get_item (pamh, PAM_CONV, (CONST_ARG void **) &conv);
3347   if (rc == PAM_SUCCESS)
3348     {
3349       rc = conv->conv (1,
3350 		       (CONST_ARG struct pam_message **) pmsg,
3351 		       &resp, conv->appdata_ptr);
3352     }
3353   else
3354     {
3355       return rc;
3356     }
3357 
3358   if (resp != NULL)
3359     {
3360       if ((flags & PAM_DISALLOW_NULL_AUTHTOK) && resp[0].resp == NULL)
3361 	{
3362 	  free (resp);
3363 	  return PAM_AUTH_ERR;
3364 	}
3365 
3366       p = resp[0].resp;
3367       /* leak if resp[0].resp is malloced. */
3368       resp[0].resp = NULL;
3369     }
3370   else
3371     {
3372       return PAM_CONV_ERR;
3373     }
3374 
3375   free (resp);
3376   pam_set_item (pamh, PAM_AUTHTOK, p);
3377 
3378   return PAM_SUCCESS;
3379 }
3380 
3381 static int
_conv_sendmsg(struct pam_conv * aconv,const char * message,int style,int no_warn)3382 _conv_sendmsg (struct pam_conv *aconv,
3383 	       const char *message, int style, int no_warn)
3384 {
3385   struct pam_message msg, *pmsg;
3386   struct pam_response *resp;
3387 
3388   if (no_warn)
3389     return PAM_SUCCESS;
3390 
3391   pmsg = &msg;
3392 
3393   msg.msg_style = style;
3394   msg.msg = (char *) message;
3395   resp = NULL;
3396 
3397   return aconv->conv (1,
3398 		      (CONST_ARG struct pam_message **) &pmsg,
3399 		      &resp, aconv->appdata_ptr);
3400 }
3401 
3402 PAM_EXTERN int
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)3403 pam_sm_authenticate (pam_handle_t * pamh,
3404 		     int flags, int argc, const char **argv)
3405 {
3406   int rc;
3407   const char *username;
3408   char *p;
3409   int use_first_pass = 0, try_first_pass = 0, ignore_flags = 0, migrate = 0;
3410   int i;
3411   pam_ldap_session_t *session = NULL;
3412   const char *configFile = NULL;
3413 
3414   for (i = 0; i < argc; i++)
3415     {
3416       if (!strcmp (argv[i], "use_first_pass"))
3417 	use_first_pass = 1;
3418       else if (!strcmp (argv[i], "try_first_pass"))
3419 	try_first_pass = 1;
3420       else if (!strncmp (argv[i], "config=", 7))
3421 	configFile = argv[i] + 7;
3422       else if (!strcmp (argv[i], "ignore_unknown_user"))
3423 	ignore_flags |= IGNORE_UNKNOWN_USER;
3424       else if (!strcmp (argv[i], "ignore_authinfo_unavail"))
3425 	ignore_flags |= IGNORE_AUTHINFO_UNAVAIL;
3426       else if (!strcmp (argv[i], "no_warn"))
3427 	;
3428       else if (!strcmp (argv[i], "debug"))
3429 	;
3430       else if (!strcmp (argv[i], "migrate"))
3431         migrate = 1;
3432       else
3433 	syslog (LOG_ERR, "illegal option %s", argv[i]);
3434     }
3435 
3436   rc = pam_get_user (pamh, (CONST_ARG char **) &username, NULL);
3437   if (rc != PAM_SUCCESS)
3438     return rc;
3439 
3440   rc = _pam_ldap_get_session (pamh, username, configFile, &session);
3441   if (rc != PAM_SUCCESS)
3442     return rc;
3443 
3444   rc = pam_get_item (pamh, PAM_AUTHTOK, (CONST_ARG void **) &p);
3445   /* start of migrate facility in "pam_ldap authentication" */
3446   if (migrate==1 && rc==PAM_SUCCESS)
3447     {
3448       /* check if specified username exists in LDAP */
3449       if (_get_user_info(session,username)==PAM_SUCCESS)
3450         {
3451           /*
3452              overwrite old LDAP userPassword with a new password
3453              obtained during pam authentication process
3454              - rootbinddn and ldap.secret must be set
3455           */
3456           rc=_update_authtok(pamh,session,username,NULL,p);
3457           return PAM_IGNORE;
3458         }
3459     }
3460   /* end of migrate facility in "pam_ldap authentication" */
3461   if (rc == PAM_SUCCESS && (use_first_pass || try_first_pass))
3462     {
3463       rc = _do_authentication (pamh, session, username, p);
3464       if (rc == PAM_SUCCESS || use_first_pass)
3465 	{
3466 	  STATUS_MAP_IGNORE_POLICY (rc, ignore_flags);
3467 
3468 	  if (rc == PAM_SUCCESS && session->info->tmpluser != NULL &&
3469 	      session->conf->tmpluser != NULL &&
3470 	      strcmp (session->info->tmpluser, session->conf->tmpluser) == 0)
3471 	    {
3472 	      (void) pam_set_data (pamh, PADL_LDAP_AUTH_DATA,
3473 				   (void *) strdup (session->info->username),
3474 				   _cleanup_data);
3475 	      rc =
3476 		pam_set_item (pamh, PAM_USER,
3477 			      (void *) session->info->tmpluser);
3478 	    }
3479           else if (rc == PAM_SUCCESS && session->info->username != NULL)
3480             {
3481               (void) pam_set_data (pamh, PADL_LDAP_AUTH_DATA,
3482                                   (void *) strdup (session->info->username),
3483                                   _cleanup_data);
3484               rc = pam_set_item (pamh, PAM_USER, (void *) session->info->username);
3485             }
3486 	  return rc;
3487 	}
3488     }
3489 
3490   /* can prompt for authentication token */
3491   rc = _get_authtok (pamh, flags, (p == NULL) ? 1 : 0);
3492   if (rc != PAM_SUCCESS)
3493     return rc;
3494 
3495   rc = pam_get_item (pamh, PAM_AUTHTOK, (CONST_ARG void **) &p);
3496   if (rc == PAM_SUCCESS)
3497     rc = _do_authentication (pamh, session, username, p);
3498   STATUS_MAP_IGNORE_POLICY (rc, ignore_flags);
3499 
3500   /*
3501    * reset username to template user if necessary
3502    * FreeBSD pam_radius does this in pam_sm_authenticate() but
3503    * I think pam_sm_acct_mgmt() is the right place.
3504    */
3505   if (rc == PAM_SUCCESS && session->info->tmpluser != NULL &&
3506       session->conf->tmpluser != NULL &&
3507       strcmp (session->info->tmpluser, session->conf->tmpluser) == 0)
3508     {
3509       /* keep original username for posterity */
3510 
3511       (void) pam_set_data (pamh, PADL_LDAP_AUTH_DATA,
3512 			   (void *) strdup (session->info->username),
3513 			   _cleanup_data);
3514       rc = pam_set_item (pamh, PAM_USER, (void *) session->info->tmpluser);
3515     }
3516   else if (rc == PAM_SUCCESS && session->info->username != NULL)
3517     {
3518       (void) pam_set_data (pamh, PADL_LDAP_AUTH_DATA,
3519                           (void *) strdup (session->info->username),
3520                           _cleanup_data);
3521       rc = pam_set_item (pamh, PAM_USER, (void *) session->info->username);
3522     }
3523 
3524   return rc;
3525 }
3526 
3527 PAM_EXTERN int
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char ** argv)3528 pam_sm_setcred (pam_handle_t * pamh, int flags, int argc, const char **argv)
3529 {
3530   return PAM_SUCCESS;
3531 }
3532 
3533 PAM_EXTERN int
pam_sm_open_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)3534 pam_sm_open_session (pam_handle_t * pamh,
3535 		     int flags, int argc, const char **argv)
3536 {
3537   /*
3538    * Bug #120 fix: close the LDAP connection as it may time out
3539    * before pam_sm_close_session() is called.
3540    */
3541   void *session;
3542 
3543   if (pam_get_data
3544       (pamh, PADL_LDAP_SESSION_DATA, (const void **) &session) == PAM_SUCCESS)
3545     pam_set_data (pamh, PADL_LDAP_SESSION_DATA, NULL, NULL);
3546 
3547   return PAM_SUCCESS;
3548 }
3549 
3550 PAM_EXTERN int
pam_sm_close_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)3551 pam_sm_close_session (pam_handle_t * pamh,
3552 		      int flags, int argc, const char **argv)
3553 {
3554   return PAM_SUCCESS;
3555 }
3556 
3557 PAM_EXTERN int
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)3558 pam_sm_chauthtok (pam_handle_t * pamh, int flags, int argc, const char **argv)
3559 {
3560   int rc = PAM_SUCCESS;
3561   char *username, *curpass = NULL, *newpass = NULL, *expuser = NULL;
3562   char buf[32], *strvals[2];
3563   struct pam_conv *appconv;
3564   struct pam_message msg, *pmsg;
3565   struct pam_response *resp;
3566   const char *cmiscptr = NULL;
3567   int tries = 0, i, canabort = 1;
3568   pam_ldap_session_t *session = NULL;
3569   int use_first_pass = 0, try_first_pass = 0, no_warn = 0;
3570   int use_authtok = 0, ignore_flags = 0;
3571   char errmsg[1024];
3572   pam_ldap_password_policy_t policy;
3573   LDAPMod *mods[2], mod;
3574   const char *configFile = NULL;
3575 
3576   for (i = 0; i < argc; i++)
3577     {
3578       if (!strcmp (argv[i], "use_first_pass"))
3579 	use_first_pass = 1;
3580       else if (!strcmp (argv[i], "try_first_pass"))
3581 	try_first_pass = 1;
3582       else if (!strncmp (argv[i], "config=", 7))
3583 	configFile = argv[i] + 7;
3584       else if (!strcmp (argv[i], "no_warn"))
3585 	no_warn = 1;
3586       else if (!strcmp (argv[i], "ignore_unknown_user"))
3587 	ignore_flags |= IGNORE_UNKNOWN_USER;
3588       else if (!strcmp (argv[i], "ignore_authinfo_unavail"))
3589 	ignore_flags |= IGNORE_AUTHINFO_UNAVAIL;
3590       else if (!strcmp (argv[i], "debug"))
3591 	;
3592       else if (!strcmp (argv[i], "use_authtok"))
3593 	use_authtok = 1;
3594       else
3595 	syslog (LOG_ERR, "illegal option %s", argv[i]);
3596     }
3597 
3598   if (flags & PAM_SILENT)
3599     no_warn = 1;
3600 
3601   rc = pam_get_item (pamh, PAM_CONV, (CONST_ARG void **) &appconv);
3602   if (rc != PAM_SUCCESS)
3603     return rc;
3604 
3605   /*
3606    * Call pam_get_data() to see whether the pre-mapped
3607    * (non-template) user is available to us. If so,
3608    * use that instead.
3609    */
3610   rc = pam_get_data (pamh, PADL_LDAP_AUTH_DATA, (const void **) &username);
3611   if (rc != PAM_SUCCESS)
3612     {
3613       rc = pam_get_user (pamh, (CONST_ARG char **) &username, NULL);
3614       if (rc != PAM_SUCCESS)
3615 	return rc;
3616     }
3617 
3618   if (username == NULL)
3619     return PAM_USER_UNKNOWN;
3620 
3621   rc = pam_get_data (pamh, PADL_LDAP_AUTHTOK_DATA, (const void **) &expuser);
3622   if (rc == PAM_SUCCESS && expuser != NULL)
3623     canabort = (strcmp (username, expuser) == 0) ? 0 : 1;
3624 
3625   rc = _pam_ldap_get_session (pamh, username, configFile, &session);
3626   if (rc != PAM_SUCCESS)
3627     return rc;
3628 
3629   /* do we prohibit changes */
3630   if (session->conf->password_prohibit_message)
3631     {
3632       rc = _get_user_info (session, username);
3633       STATUS_MAP_IGNORE_POLICY (rc, ignore_flags);
3634       /* skip non-ldap users */
3635       if (rc != PAM_SUCCESS)
3636 	return rc;
3637       /* prohibit ldap users */
3638       _conv_sendmsg (appconv, session->conf->password_prohibit_message,
3639 		     PAM_ERROR_MSG, no_warn);
3640       return PAM_PERM_DENIED;
3641     }
3642 
3643   if (flags & PAM_PRELIM_CHECK)
3644     {
3645       /* see whether the user exists */
3646       rc = _get_user_info (session, username);
3647       STATUS_MAP_IGNORE_POLICY (rc, ignore_flags);
3648       if (rc != PAM_SUCCESS)
3649 	return rc;
3650 
3651       if (!(session->conf->rootbinddn && getuid () == 0))
3652 	{
3653 	  /* we are not root, authenticate old password */
3654 	  if (try_first_pass || use_first_pass)
3655 	    {
3656 	      if (pam_get_item
3657 		  (pamh, PAM_OLDAUTHTOK,
3658 		   (CONST_ARG void **) &curpass) == PAM_SUCCESS &&
3659 		  curpass != NULL)
3660 		{
3661 		  rc = _do_authentication (pamh, session, username, curpass);
3662 		  if (rc != PAM_SUCCESS)
3663 		    {
3664 		      if (use_first_pass)
3665 			{
3666 			  _conv_sendmsg (appconv, "LDAP Password incorrect",
3667 					 PAM_ERROR_MSG, no_warn);
3668 			}
3669 		      else
3670 			{
3671 			  _conv_sendmsg (appconv,
3672 					 "LDAP Password incorrect: try again",
3673 					 PAM_ERROR_MSG, no_warn);
3674 			}
3675 		      return rc;
3676 		    }
3677 		}
3678 	      else
3679 		{
3680 		  curpass = NULL;
3681 		}
3682 	    }
3683 
3684 	  tries = 0;
3685 
3686 	  /* support Netscape Directory Server's password policy */
3687 	  rc = _get_password_policy (session, &policy);
3688 	  if (rc != PAM_SUCCESS)
3689 	    return rc;
3690 
3691 	  while ((curpass == NULL) && (tries++ < policy.password_max_failure))
3692 	    {
3693 	      pmsg = &msg;
3694 	      msg.msg_style = PAM_PROMPT_ECHO_OFF;
3695 	      msg.msg = OLD_PASSWORD_PROMPT;
3696 	      resp = NULL;
3697 
3698 	      rc = appconv->conv (1, (CONST_ARG struct pam_message **) &pmsg,
3699 				  &resp, appconv->appdata_ptr);
3700 
3701 	      if (rc != PAM_SUCCESS)
3702 		return rc;
3703 
3704 	      curpass = resp->resp;
3705 	      free (resp);
3706 
3707 	      /* authenticate the old password */
3708 	      rc = _do_authentication (pamh, session, username, curpass);
3709 	      if (rc != PAM_SUCCESS)
3710 		{
3711 		  int abortme = 0;
3712 
3713 		  if (curpass != NULL && curpass[0] == '\0')
3714 		    abortme = 1;
3715 
3716 		  _pam_overwrite (curpass);
3717 		  _pam_drop (curpass);
3718 
3719 		  if (canabort && abortme)
3720 		    {
3721 		      _conv_sendmsg (appconv, "Password change aborted",
3722 				     PAM_ERROR_MSG, no_warn);
3723 		      return PAM_AUTHTOK_RECOVERY_ERR;
3724 		    }
3725 		  else
3726 		    {
3727 		      _conv_sendmsg (appconv,
3728 				     "LDAP Password incorrect: try again",
3729 				     PAM_ERROR_MSG, no_warn);
3730 		    }
3731 		}
3732 	    }			/* while */
3733 
3734 	  if (curpass == NULL)
3735 	    return PAM_MAXTRIES;	/* maximum tries exceeded */
3736 	  else
3737 	    pam_set_item (pamh, PAM_OLDAUTHTOK, (void *) strdup(curpass));
3738 	}
3739       else
3740 	{
3741 	  /* we are root */
3742 	  curpass = NULL;
3743 	}
3744 
3745       pam_set_data (pamh, PADL_LDAP_OLDAUTHTOK_DATA,
3746 		    (curpass == NULL) ? NULL : (void *) strdup (curpass),
3747 		    _cleanup_authtok_data);
3748       return rc;
3749     }				/* prelim */
3750   else if (session->info == NULL)	/* this is no LDAP user */
3751     return (ignore_flags & IGNORE_UNKNOWN_USER) ? PAM_IGNORE :
3752       PAM_USER_UNKNOWN;
3753 
3754 
3755   if (use_authtok)
3756     use_first_pass = 1;
3757 
3758   rc =
3759     pam_get_data (pamh, PADL_LDAP_OLDAUTHTOK_DATA, (const void **) &curpass);
3760   if (rc != PAM_SUCCESS)
3761     {
3762       syslog (LOG_ERR,
3763 	      "pam_ldap: error getting old authentication token (%s)",
3764 	      pam_strerror (pamh, rc));
3765       return PAM_AUTHTOK_RECOVERY_ERR;
3766     }
3767 
3768   if (try_first_pass || use_first_pass)
3769     {
3770       if (pam_get_item (pamh, PAM_AUTHTOK, (CONST_ARG void **) &newpass) !=
3771 	  PAM_SUCCESS)
3772 	newpass = NULL;
3773 
3774       if (use_first_pass && newpass == NULL)
3775 	return PAM_AUTHTOK_RECOVERY_ERR;
3776     }
3777 
3778   tries = 0;
3779 
3780   /* support Netscape Directory Server's password policy */
3781   rc = _get_password_policy (session, &policy);
3782   if (rc != PAM_SUCCESS)
3783     return rc;
3784 
3785   while ((newpass == NULL) && (tries++ < policy.password_max_failure))
3786     {
3787       pmsg = &msg;
3788       msg.msg_style = PAM_PROMPT_ECHO_OFF;
3789       msg.msg = NEW_PASSWORD_PROMPT;
3790       resp = NULL;
3791 
3792       rc = appconv->conv (1, (CONST_ARG struct pam_message **) &pmsg,
3793 			  &resp, appconv->appdata_ptr);
3794 
3795       if (rc != PAM_SUCCESS)
3796 	return rc;
3797 
3798       newpass = resp->resp;
3799       free (resp);
3800 
3801       if (newpass != NULL && newpass[0] == '\0')
3802 	{
3803 	  free (newpass);
3804 	  newpass = NULL;
3805 	}
3806 
3807       if (newpass != NULL)
3808 	{
3809 	  if (getuid () != 0)
3810 	    {
3811 	      if (curpass != NULL && !strcmp (curpass, newpass))
3812 		{
3813 		  cmiscptr = "Passwords must differ";
3814 		  newpass = NULL;
3815 		}
3816 	      else if (strlen (newpass) < (size_t) policy.password_min_length)
3817 		{
3818 		  cmiscptr = "Password too short";
3819 		  newpass = NULL;
3820 		}
3821 	    }
3822 	}
3823       else
3824 	{
3825 	  return PAM_AUTHTOK_RECOVERY_ERR;
3826 	}
3827 
3828       if (cmiscptr == NULL)
3829 	{
3830 	  /* get password again */
3831 	  char *miscptr = NULL;
3832 
3833 	  pmsg = &msg;
3834 	  msg.msg_style = PAM_PROMPT_ECHO_OFF;
3835 	  msg.msg = AGAIN_PASSWORD_PROMPT;
3836 	  resp = NULL;
3837 
3838 	  rc = appconv->conv (1, (CONST_ARG struct pam_message **) &pmsg,
3839 			      &resp, appconv->appdata_ptr);
3840 
3841 	  if (rc == PAM_SUCCESS)
3842 	    {
3843 	      miscptr = resp->resp;
3844 	      free (resp);
3845 	      if (miscptr[0] == '\0')
3846 		{
3847 		  free (miscptr);
3848 		  miscptr = NULL;
3849 		}
3850 	    }
3851 	  if (miscptr == NULL)
3852 	    {
3853 	      if (canabort)
3854 		{
3855 		  _conv_sendmsg (appconv, "Password change aborted",
3856 				 PAM_ERROR_MSG, no_warn);
3857 		  return PAM_AUTHTOK_RECOVERY_ERR;
3858 		}
3859 	    }
3860 	  else if (!strcmp (newpass, miscptr))
3861 	    {
3862 	      miscptr = NULL;
3863 	      break;
3864 	    }
3865 
3866 	  _conv_sendmsg (appconv, "You must enter the same password",
3867 			 PAM_ERROR_MSG, no_warn);
3868 	  miscptr = NULL;
3869 	  newpass = NULL;
3870 	}
3871       else
3872 	{
3873 	  _conv_sendmsg (appconv, cmiscptr, PAM_ERROR_MSG, no_warn);
3874 	  cmiscptr = NULL;
3875 	  newpass = NULL;
3876 	}
3877     }				/* while */
3878 
3879   if (cmiscptr != NULL || newpass == NULL)
3880     return PAM_MAXTRIES;
3881 
3882   rc = _update_authtok (pamh, session, username, curpass, newpass);
3883   if (rc != PAM_SUCCESS)
3884     {
3885       int lderr;
3886       char *reason = NULL;
3887 
3888       lderr = ldap_get_lderrno (session->ld, NULL, &reason);
3889       if (reason != NULL)
3890 	snprintf (errmsg, sizeof errmsg,
3891 		  "LDAP password information update failed: %s\n%s",
3892 		  ldap_err2string (lderr), reason);
3893       else
3894 	snprintf (errmsg, sizeof errmsg,
3895 		  "LDAP password information update failed: %s",
3896 		  ldap_err2string (lderr));
3897 
3898       _conv_sendmsg (appconv, errmsg, PAM_ERROR_MSG, no_warn);
3899     }
3900   else
3901     {
3902       /* update shadowLastChange; may fail if not shadowAccount */
3903       snprintf (buf, sizeof buf, "%ld", time (NULL) / (60 * 60 * 24));
3904       strvals[0] = buf;
3905       strvals[1] = NULL;
3906 
3907       mod.mod_values = strvals;
3908       mod.mod_type = (char *) "shadowLastChange";
3909       mod.mod_op = LDAP_MOD_REPLACE;
3910 
3911       mods[0] = &mod;
3912       mods[1] = NULL;
3913 
3914       /* do this silently because it may fail */
3915       (void) ldap_modify_s (session->ld, session->info->userdn, mods);
3916 
3917       snprintf (errmsg, sizeof errmsg,
3918 		"LDAP password information changed for %s", username);
3919       _conv_sendmsg (appconv, errmsg, PAM_TEXT_INFO,
3920 		     (flags & PAM_SILENT) ? 1 : 0);
3921       session->info->policy_error = POLICY_ERROR_SUCCESS;
3922     }
3923 
3924   pam_set_item (pamh, PAM_AUTHTOK, (void *) newpass);
3925 
3926   return rc;
3927 }
3928 
3929 PAM_EXTERN int
pam_sm_acct_mgmt(pam_handle_t * pamh,int flags,int argc,const char ** argv)3930 pam_sm_acct_mgmt (pam_handle_t * pamh, int flags, int argc, const char **argv)
3931 {
3932   /*
3933    * check whether the user can login.
3934    * returns one of:
3935    *   PAM_ACCT_EXPIRED (account expired)
3936    *   PAM_PERM_DENIED (authorization failed)
3937    *   PAM_AUTHTOKEN_REQD (authtoken expired)
3938    *   PAM_USER_UNKNOWN
3939    */
3940   int rc;
3941   const char *username;
3942   int no_warn = 0, ignore_flags = 0;
3943   int i, success = PAM_SUCCESS;
3944   struct pam_conv *appconv;
3945   pam_ldap_session_t *session = NULL;
3946   char buf[1024];
3947   time_t currenttime;
3948   long int currentday;
3949   long int expirein = 0;	/* seconds until password expires */
3950   const char *configFile = NULL;
3951 
3952   for (i = 0; i < argc; i++)
3953     {
3954       if (!strcmp (argv[i], "use_first_pass"))
3955 	;
3956       else if (!strcmp (argv[i], "try_first_pass"))
3957 	;
3958       else if (!strncmp (argv[i], "config=", 7))
3959 	configFile = argv[i] + 7;
3960       else if (!strcmp (argv[i], "no_warn"))
3961 	no_warn = 1;
3962       else if (!strcmp (argv[i], "ignore_unknown_user"))
3963 	ignore_flags |= IGNORE_UNKNOWN_USER;
3964       else if (!strcmp (argv[i], "ignore_authinfo_unavail"))
3965 	ignore_flags |= IGNORE_AUTHINFO_UNAVAIL;
3966       else if (!strcmp (argv[i], "debug"))
3967 	;
3968       else
3969 	syslog (LOG_ERR, "illegal option %s", argv[i]);
3970     }
3971 
3972   if (flags & PAM_SILENT)
3973     no_warn = 1;
3974 
3975   rc = pam_get_item (pamh, PAM_CONV, (CONST_ARG void **) &appconv);
3976   if (rc != PAM_SUCCESS)
3977     return rc;
3978 
3979   /*
3980    * Call pam_get_data() to see whether the pre-mapped
3981    * (non-template) user is available to us. If so,
3982    * use that instead.
3983    */
3984   rc = pam_get_data (pamh, PADL_LDAP_AUTH_DATA, (const void **) &username);
3985   if (rc != PAM_SUCCESS)
3986     {
3987       rc = pam_get_user (pamh, (CONST_ARG char **) &username, NULL);
3988       if (rc != PAM_SUCCESS)
3989 	return rc;
3990     }
3991 
3992   if (username == NULL)
3993     return PAM_USER_UNKNOWN;
3994 
3995   rc = _pam_ldap_get_session (pamh, username, configFile, &session);
3996   if (rc != PAM_SUCCESS)
3997     {
3998       return rc;
3999     }
4000 
4001   if (session->info == NULL)
4002     {
4003       rc = _get_user_info (session, username);
4004       if (rc != PAM_SUCCESS)
4005 	{
4006 	  STATUS_MAP_IGNORE_POLICY (rc, ignore_flags);
4007 
4008 	  return rc;
4009 	}
4010     }
4011 
4012   /* Grab the current time */
4013   time (&currenttime);
4014   currentday = (long int) (currenttime / SECSPERDAY);
4015 
4016   /* Check shadow expire conditions */
4017   /* Do we have an absolute expiry date? */
4018   if (session->info->shadow.expire > 0)
4019     {
4020       if (currentday >= session->info->shadow.expire)
4021 	{
4022 	  return PAM_ACCT_EXPIRED;
4023 	}
4024     }
4025 
4026   if (session->info->shadow.lstchg == 0)
4027     {
4028       /*
4029        * Adhere to convention of a shadow last change
4030        * value of 0 implying that the password has
4031        * expired. Apparently this is documented in the
4032        * shadow suite (libmisc/isexpired.c).
4033        */
4034       session->info->policy_error = POLICY_ERROR_PASSWORD_EXPIRED;
4035     }
4036 
4037   /*
4038    * Also check if user hasn't changed password for the inactive
4039    * amount of time.  This also counts as an expired account.
4040    */
4041   if ((session->info->shadow.lstchg > 0) &&
4042       (session->info->shadow.max > 0) && (session->info->shadow.inact > 0))
4043     {
4044       if (currentday >= (session->info->shadow.lstchg +
4045 			 session->info->shadow.max +
4046 			 session->info->shadow.inact))
4047 	{
4048 	  return PAM_ACCT_EXPIRED;
4049 	}
4050     }
4051 
4052   /* Our shadow information should be populated, so do some calculations */
4053   if ((session->info->shadow.lstchg > 0) && (session->info->shadow.max > 0))
4054     {
4055       if (currentday >= (session->info->shadow.lstchg +
4056 			 session->info->shadow.max))
4057 	{
4058 	  session->info->policy_error = POLICY_ERROR_PASSWORD_EXPIRED;
4059 	}
4060     }
4061 
4062   /* check whether the password has expired */
4063   switch (session->info->policy_error)
4064     {
4065     case POLICY_ERROR_SUCCESS:
4066       break;
4067     case POLICY_ERROR_PASSWORD_EXPIRED:
4068     case POLICY_ERROR_CHANGE_AFTER_RESET:
4069       _conv_sendmsg (appconv,
4070 		     "You are required to change your LDAP password immediately.",
4071 		     PAM_ERROR_MSG, no_warn);
4072 #ifdef LINUX
4073       rc = success = PAM_AUTHTOKEN_REQD;
4074 #else
4075       rc = success = PAM_NEW_AUTHTOK_REQD;
4076 #endif /* LINUX */
4077       break;
4078     case POLICY_ERROR_ACCOUNT_LOCKED:
4079     case POLICY_ERROR_PASSWORD_MOD_NOT_ALLOWED:
4080     case POLICY_ERROR_MUST_SUPPLY_OLD_PASSWORD:
4081     case POLICY_ERROR_INSUFFICIENT_PASSWORD_QUALITY:
4082     case POLICY_ERROR_PASSWORD_TOO_SHORT:
4083     case POLICY_ERROR_PASSWORD_TOO_YOUNG:
4084     case POLICY_ERROR_PASSWORD_INSUFFICIENT:
4085       _conv_sendmsg (appconv,
4086 		     policy_error_table[session->info->policy_error],
4087 		     PAM_ERROR_MSG, no_warn);
4088       return PAM_PERM_DENIED;
4089       break;
4090     default:
4091       snprintf (buf, sizeof buf,
4092 		"Unknown password policy error %d received.",
4093 		session->info->policy_error);
4094       _conv_sendmsg (appconv, buf, PAM_ERROR_MSG, no_warn);
4095       return PAM_PERM_DENIED;
4096       break;
4097     }
4098 
4099   /*
4100    * Warnings.  First, check if we've got a non-zero warning time
4101    * in the shadow struct.  If so, we're a shadow account, and set
4102    * things accordingly.  Otherwise, check the Netscape controls.
4103    */
4104 
4105   /*
4106    * If the password's expired, no sense warning
4107    */
4108   if (session->info->policy_error != POLICY_ERROR_PASSWORD_EXPIRED)
4109     {
4110       if (session->info->shadow.warn > 0)	/* shadowAccount */
4111 	{
4112 	  /*
4113 	   * Are we within warning period?
4114 	   */
4115 
4116 	  expirein = session->info->shadow.lstchg +
4117 	    session->info->shadow.max - currentday;
4118 
4119 	  if (session->info->shadow.warn <= expirein)
4120 	    {
4121 	      expirein = 0;	/* Not within warning period yet */
4122 	    }
4123 	}
4124       else
4125 	{
4126 	  expirein = session->info->password_expiration_time / SECSPERDAY;
4127 	}
4128 
4129       if (expirein > 0)
4130 	{
4131 	  snprintf (buf, sizeof buf,
4132 		    "Your LDAP password will expire in %ld day%s.",
4133 		    expirein, (expirein == 1) ? "" : "s");
4134 	  _conv_sendmsg (appconv, buf, PAM_ERROR_MSG, no_warn);
4135 
4136 	  /* we set this to make sure that user can't abort a password change */
4137 	  (void) pam_set_data (pamh, PADL_LDAP_AUTHTOK_DATA,
4138 			       (void *) strdup (username), _cleanup_data);
4139 	}
4140     }				/* password expired */
4141 
4142   /* group auth, per Chris's pam_ldap_auth module */
4143   if (session->conf->groupdn != NULL)
4144     {
4145       rc = ldap_compare_s (session->ld,
4146 			   session->conf->groupdn,
4147 			   session->conf->groupattr, session->info->userdn);
4148       if (rc != LDAP_COMPARE_TRUE)
4149 	{
4150 	  snprintf (buf, sizeof buf, "You must be a %s of %s to login.",
4151 		    session->conf->groupattr, session->conf->groupdn);
4152 	  _conv_sendmsg (appconv, buf, PAM_ERROR_MSG, no_warn);
4153 	  return PAM_PERM_DENIED;
4154 	}
4155       else
4156 	rc = success;
4157     }
4158 
4159   if (rc == success && session->conf->checkserviceattr)
4160     {
4161       rc = _service_ok (pamh, session);
4162       if (rc != PAM_SUCCESS)
4163 	_conv_sendmsg (appconv, "Access denied for this service",
4164 		       PAM_ERROR_MSG, no_warn);
4165       else
4166 	rc = success;
4167     }
4168 
4169   if (rc == success && session->conf->checkhostattr)
4170     {
4171       rc = _host_ok (session);
4172       if (rc != PAM_SUCCESS)
4173 	_conv_sendmsg (appconv, "Access denied for this host", PAM_ERROR_MSG,
4174 		       no_warn);
4175       else
4176 	rc = success;
4177     }
4178 
4179   if (rc == success && session->conf->min_uid
4180       && session->info->uid < session->conf->min_uid)
4181     {
4182       snprintf (buf, sizeof buf, "UID must be greater than %ld",
4183 		(long) session->conf->min_uid);
4184       _conv_sendmsg (appconv, buf, PAM_ERROR_MSG, no_warn);
4185       return PAM_PERM_DENIED;
4186     }
4187 
4188   if (rc == success && session->conf->max_uid
4189       && session->info->uid > session->conf->max_uid)
4190     {
4191       snprintf (buf, sizeof buf, "UID must be less than %ld",
4192 		(long) session->conf->max_uid);
4193       _conv_sendmsg (appconv, buf, PAM_ERROR_MSG, no_warn);
4194       return PAM_PERM_DENIED;
4195     }
4196 
4197   return rc;
4198 }
4199 
4200 /* static module data */
4201 #ifdef PAM_STATIC
4202 struct pam_module _modstruct = {
4203   "pam_ldap",
4204   pam_sm_authenticate,
4205   pam_sm_setcred,
4206   pam_sm_acct_mgmt,
4207   pam_sm_open_session,
4208   pam_sm_close_session,
4209   pam_sm_chauthtok
4210 };
4211 #endif /* PAM_STATIC */
4212