1 /*
2    pam.c - pam module functions
3 
4    Copyright (C) 2009 Howard Chu
5    Copyright (C) 2009-2015 Arthur de Jong
6 
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11 
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16 
17    You should have received a copy of the GNU Lesser General Public
18    License along with this library; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301 USA
21 */
22 
23 #include "config.h"
24 
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <syslog.h>
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include <pwd.h>
32 
33 /* these are defined (before including pam_modules.h) for staticly linking */
34 #define PAM_SM_AUTH
35 #define PAM_SM_ACCOUNT
36 #define PAM_SM_SESSION
37 #define PAM_SM_PASSWORD
38 
39 #include "common.h"
40 #include "compat/attrs.h"
41 #include "compat/pam_compat.h"
42 
43 #ifdef HAVE_SECURITY_PAM_APPL_H
44 #include <security/pam_appl.h>
45 #endif /* HAVE_SECURITY_PAM_APPL_H */
46 #ifndef HAVE_PAM_PAM_MODULES_H
47 #include <security/pam_modules.h>
48 #ifdef HAVE_SECURITY_PAM_EXT_H
49 #include <security/pam_ext.h>
50 #endif /* HAVE_SECURITY_PAM_EXT_H */
51 #else /* not HAVE_PAM_PAM_MODULES_H */
52 #include <pam/pam_modules.h>
53 #endif /* not HAVE_PAM_PAM_MODULES_H */
54 
55 /* the name we store our context under */
56 #define PLD_CTX "PAM_LDAPD_CTX"
57 
58 /* structure that stores the results for an nslcd call */
59 struct nslcd_resp {
60   int res;
61   char msg[1024];
62 };
63 
64 /* this struct represents the context that the PAM module keeps
65    between calls */
66 struct pld_ctx {
67   char *username;
68   struct nslcd_resp saved_authz;
69   struct nslcd_resp saved_session;
70   int asroot;
71   char *oldpassword;
72 };
73 
74 /* clear the context to all empty values */
ctx_clear(struct pld_ctx * ctx)75 static void ctx_clear(struct pld_ctx *ctx)
76 {
77   if (ctx->username)
78   {
79     free(ctx->username);
80     ctx->username = NULL;
81   }
82   ctx->saved_authz.res = PAM_SUCCESS;
83   memset(ctx->saved_authz.msg, 0, sizeof(ctx->saved_authz.msg));
84   ctx->saved_session.res = PAM_SUCCESS;
85   memset(ctx->saved_session.msg, 0, sizeof(ctx->saved_session.msg));
86   ctx->asroot = 0;
87   if (ctx->oldpassword)
88   {
89     memset(ctx->oldpassword, 0, strlen(ctx->oldpassword));
90     free(ctx->oldpassword);
91     ctx->oldpassword = NULL;
92   }
93 }
94 
95 /* free the context (this is installed as handler into PAM) */
ctx_free(pam_handle_t UNUSED (* pamh),void * data,int UNUSED (err))96 static void ctx_free(pam_handle_t UNUSED(*pamh), void *data, int UNUSED(err))
97 {
98   struct pld_ctx *ctx = data;
99   ctx_clear(ctx);
100   free(ctx);
101 }
102 
103 /* try to get the module's context, returns a PAM status code */
ctx_get(pam_handle_t * pamh,const char * username,struct pld_ctx ** pctx)104 static int ctx_get(pam_handle_t *pamh, const char *username, struct pld_ctx **pctx)
105 {
106   struct pld_ctx *ctx = NULL;
107   int rc;
108   /* try to get the context from PAM */
109   rc = pam_get_data(pamh, PLD_CTX, (const void **)&ctx);
110   if ((rc == PAM_SUCCESS) && (ctx != NULL))
111   {
112     /* if the user is different clear the context */
113     if ((ctx->username != NULL) && (strcmp(ctx->username, username) != 0))
114       ctx_clear(ctx);
115   }
116   else
117   {
118     /* allocate a new context */
119     ctx = calloc(1, sizeof(struct pld_ctx));
120     if (ctx == NULL)
121     {
122       pam_syslog(pamh, LOG_CRIT, "calloc(): failed to allocate memory: %s",
123                  strerror(errno));
124       return PAM_BUF_ERR;
125     }
126     ctx_clear(ctx);
127     /* store the new context with the handler to free it */
128     rc = pam_set_data(pamh, PLD_CTX, ctx, ctx_free);
129     if (rc != PAM_SUCCESS)
130     {
131       ctx_free(pamh, ctx, 0);
132       pam_syslog(pamh, LOG_ERR, "failed to store context: %s",
133                  pam_strerror(pamh, rc));
134       return rc;
135     }
136   }
137   /* save the username in the context */
138   if (ctx->username == NULL)
139     ctx->username = strdup(username);
140   /* return the context */
141   *pctx = ctx;
142   return PAM_SUCCESS;
143 }
144 
145 /* our PAM module configuration */
146 struct pld_cfg {
147   int nullok;
148   int no_warn;
149   int ignore_unknown_user;
150   int ignore_authinfo_unavail;
151   int debug;
152   uid_t minimum_uid;
153 };
154 
cfg_init(pam_handle_t * pamh,int flags,int argc,const char ** argv,struct pld_cfg * cfg)155 static void cfg_init(pam_handle_t *pamh, int flags,
156                      int argc, const char **argv,
157                      struct pld_cfg *cfg)
158 {
159   int i;
160   /* initialise config with defaults */
161   cfg->nullok = 0;
162   cfg->no_warn = 0;
163   cfg->ignore_unknown_user = 0;
164   cfg->ignore_authinfo_unavail = 0;
165   cfg->debug = 0;
166   cfg->minimum_uid = 0;
167   /* go over arguments */
168   for (i = 0; i < argc; i++)
169   {
170     if (strcmp(argv[i], "use_first_pass") == 0)
171       /* ignore, this option is used by pam_get_authtok() internally */ ;
172     else if (strcmp(argv[i], "try_first_pass") == 0)
173       /* ignore, this option is used by pam_get_authtok() internally */ ;
174     else if (strcmp(argv[i], "nullok") == 0)
175       cfg->nullok = 1;
176     else if (strcmp(argv[i], "use_authtok") == 0)
177       /* ignore, this option is used by pam_get_authtok() internally */ ;
178     else if (strcmp(argv[i], "no_warn") == 0)
179       cfg->no_warn = 1;
180     else if (strcmp(argv[i], "ignore_unknown_user") == 0)
181       cfg->ignore_unknown_user = 1;
182     else if (strcmp(argv[i], "ignore_authinfo_unavail") == 0)
183       cfg->ignore_authinfo_unavail = 1;
184     else if (strcmp(argv[i], "debug") == 0)
185       cfg->debug = 1;
186     else if (strncmp(argv[i], "minimum_uid=", 12) == 0)
187       cfg->minimum_uid = (uid_t)atoi(argv[i] + 12);
188     else
189       pam_syslog(pamh, LOG_ERR, "unknown option: %s", argv[i]);
190   }
191   /* check flags */
192   if (flags & PAM_SILENT)
193     cfg->no_warn = 1;
194 }
195 
init(pam_handle_t * pamh,struct pld_cfg * cfg,struct pld_ctx ** ctx,const char ** username,const char ** service,const char ** ruser,const char ** rhost,const char ** tty)196 static int init(pam_handle_t *pamh, struct pld_cfg *cfg, struct pld_ctx **ctx,
197                 const char **username, const char **service, const char **ruser,
198                 const char **rhost, const char **tty)
199 {
200   int rc;
201   struct passwd *pwent;
202   /* get user name */
203   rc = pam_get_user(pamh, username, NULL);
204   if (rc != PAM_SUCCESS)
205   {
206     pam_syslog(pamh, LOG_ERR, "failed to get user name: %s", pam_strerror(pamh, rc));
207     return rc;
208   }
209   if ((*username == NULL) || ((*username)[0] == '\0'))
210   {
211     pam_syslog(pamh, LOG_ERR, "got empty user name");
212     return PAM_USER_UNKNOWN;
213   }
214   /* check uid */
215   if (cfg->minimum_uid > 0)
216   {
217     pwent = pam_modutil_getpwnam(args->pamh, *username);
218     if ((pwent != NULL) && (pwent->pw_uid < cfg->minimum_uid))
219     {
220       if (cfg->debug)
221         pam_syslog(pamh, LOG_DEBUG, "uid below minimum_uid; user=%s uid=%ld",
222                    *username, (long)pwent->pw_uid);
223       return cfg->ignore_unknown_user ? PAM_IGNORE : PAM_USER_UNKNOWN;
224     }
225   }
226   /* get our context */
227   rc = ctx_get(pamh, *username, ctx);
228   if (rc != PAM_SUCCESS)
229     return rc;
230   /* get service name */
231   rc = pam_get_item(pamh, PAM_SERVICE, (PAM_ITEM_CONST void **)service);
232   if (rc != PAM_SUCCESS)
233   {
234     pam_syslog(pamh, LOG_ERR, "failed to get service name: %s",
235                pam_strerror(pamh, rc));
236     return rc;
237   }
238   /* get more PAM information (ignore errors) */
239   pam_get_item(pamh, PAM_RUSER, (PAM_ITEM_CONST void **)ruser);
240   pam_get_item(pamh, PAM_RHOST, (PAM_ITEM_CONST void **)rhost);
241   pam_get_item(pamh, PAM_TTY, (PAM_ITEM_CONST void **)tty);
242   return PAM_SUCCESS;
243 }
244 
245 /* map a NSLCD PAM status code to a PAM status code */
nslcd2pam_rc(pam_handle_t * pamh,int rc)246 static int nslcd2pam_rc(pam_handle_t *pamh, int rc)
247 {
248 #define map(i) case NSLCD_##i: return i;
249   switch (rc)
250   {
251     map(PAM_SUCCESS);
252     map(PAM_PERM_DENIED);
253     map(PAM_AUTH_ERR);
254     map(PAM_CRED_INSUFFICIENT);
255     map(PAM_AUTHINFO_UNAVAIL);
256     map(PAM_USER_UNKNOWN);
257     map(PAM_MAXTRIES);
258     map(PAM_NEW_AUTHTOK_REQD);
259     map(PAM_ACCT_EXPIRED);
260     map(PAM_SESSION_ERR);
261     map(PAM_AUTHTOK_ERR);
262     map(PAM_AUTHTOK_DISABLE_AGING);
263     map(PAM_IGNORE);
264     map(PAM_ABORT);
265     map(PAM_AUTHTOK_EXPIRED);
266     default:
267       pam_syslog(pamh, LOG_ERR, "unknown NSLCD_PAM_* code returned: %d", rc);
268       return PAM_ABORT;
269   }
270 }
271 
272 /* check whether the specified user is handled by nslcd */
nslcd_request_exists(pam_handle_t * pamh,struct pld_cfg * cfg,const char * username)273 static int nslcd_request_exists(pam_handle_t *pamh, struct pld_cfg *cfg,
274                                 const char *username)
275 {
276   PAM_REQUEST(
277     NSLCD_ACTION_PASSWD_BYNAME,
278     /* log debug message */
279     pam_syslog(pamh, LOG_DEBUG, "nslcd account check; user=%s", username),
280     /* write the request parameters */
281     WRITE_STRING(fp, username),
282     /* read the result entry (skip it completely) */
283     SKIP_STRING(fp);            /* user name */
284     SKIP_STRING(fp);            /* passwd entry */
285     SKIP(fp, sizeof(int32_t));  /* uid */
286     SKIP(fp, sizeof(int32_t));  /* gid */
287     SKIP_STRING(fp);            /* gecos */
288     SKIP_STRING(fp);            /* home dir */
289     SKIP_STRING(fp);            /* shell */
290   )
291 }
292 
293 /* perform an authentication call over nslcd */
nslcd_request_authc(pam_handle_t * pamh,struct pld_cfg * cfg,const char * username,const char * service,const char * ruser,const char * rhost,const char * tty,const char * passwd,struct nslcd_resp * authc_resp,struct nslcd_resp * authz_resp)294 static int nslcd_request_authc(pam_handle_t *pamh, struct pld_cfg *cfg,
295                                const char *username, const char *service,
296                                const char *ruser, const char *rhost,
297                                const char *tty, const char *passwd,
298                                struct nslcd_resp *authc_resp,
299                                struct nslcd_resp *authz_resp)
300 {
301   PAM_REQUEST(
302     NSLCD_ACTION_PAM_AUTHC,
303     /* log debug message */
304     pam_syslog(pamh, LOG_DEBUG, "nslcd authentication; user=%s", username),
305     /* write the request parameters */
306     WRITE_STRING(fp, username);
307     WRITE_STRING(fp, service);
308     WRITE_STRING(fp, ruser);
309     WRITE_STRING(fp, rhost);
310     WRITE_STRING(fp, tty);
311     WRITE_STRING(fp, passwd),
312     /* read the result entry */
313     READ_PAM_CODE(fp, authc_resp->res);
314     READ_STRING(fp, authc_resp->msg); /* user name */
315     /* if we want the authorisation response, save it, otherwise skip it */
316     if (authz_resp != NULL)
317     {
318       READ_PAM_CODE(fp, authz_resp->res);
319       READ_STRING(fp, authz_resp->msg);
320     }
321     else
322     {
323       SKIP(fp, sizeof(int32_t));
324       SKIP_STRING(fp);
325     }
326   )
327 }
328 
329 /* perform an authorisation call over nslcd */
nslcd_request_authz(pam_handle_t * pamh,struct pld_cfg * cfg,const char * username,const char * service,const char * ruser,const char * rhost,const char * tty,struct nslcd_resp * resp)330 static int nslcd_request_authz(pam_handle_t *pamh, struct pld_cfg *cfg,
331                                const char *username, const char *service,
332                                const char *ruser, const char *rhost,
333                                const char *tty, struct nslcd_resp *resp)
334 {
335   PAM_REQUEST(
336     NSLCD_ACTION_PAM_AUTHZ,
337     /* log debug message */
338     pam_syslog(pamh, LOG_DEBUG, "nslcd authorisation; user=%s", username),
339     /* write the request parameters */
340     WRITE_STRING(fp, username);
341     WRITE_STRING(fp, service);
342     WRITE_STRING(fp, ruser);
343     WRITE_STRING(fp, rhost);
344     WRITE_STRING(fp, tty),
345     /* read the result entry */
346     READ_PAM_CODE(fp, resp->res);
347     READ_STRING(fp, resp->msg);
348   )
349 }
350 
351 /* do a session open nslcd request */
nslcd_request_sess_o(pam_handle_t * pamh,struct pld_cfg * cfg,const char * username,const char * service,const char * ruser,const char * rhost,const char * tty,struct nslcd_resp * resp)352 static int nslcd_request_sess_o(pam_handle_t *pamh, struct pld_cfg *cfg,
353                                 const char *username, const char *service,
354                                 const char *ruser, const char *rhost,
355                                 const char *tty, struct nslcd_resp *resp)
356 {
357   PAM_REQUEST(
358     NSLCD_ACTION_PAM_SESS_O,
359     /* log debug message */
360     pam_syslog(pamh, LOG_DEBUG, "nslcd session open; user=%s", username),
361     /* write the request parameters */
362     WRITE_STRING(fp, username);
363     WRITE_STRING(fp, service);
364     WRITE_STRING(fp, ruser);
365     WRITE_STRING(fp, rhost);
366     WRITE_STRING(fp, tty),
367     /* read the result entry */
368     READ_STRING(fp, resp->msg)
369   )
370 }
371 
372 /* do a session close nslcd request */
nslcd_request_sess_c(pam_handle_t * pamh,struct pld_cfg * cfg,const char * username,const char * service,const char * ruser,const char * rhost,const char * tty,const char * sessid)373 static int nslcd_request_sess_c(pam_handle_t *pamh, struct pld_cfg *cfg,
374                                 const char *username, const char *service,
375                                 const char *ruser, const char *rhost,
376                                 const char *tty, const char *sessid)
377 {
378   PAM_REQUEST(
379     NSLCD_ACTION_PAM_SESS_C,
380     /* log debug message */
381     pam_syslog(pamh, LOG_DEBUG, "nslcd session close; user=%s", username),
382     /* write the request parameters */
383     WRITE_STRING(fp, username);
384     WRITE_STRING(fp, service);
385     WRITE_STRING(fp, ruser);
386     WRITE_STRING(fp, rhost);
387     WRITE_STRING(fp, tty);
388     WRITE_STRING(fp, sessid),
389     /* no result entry to read */ ;
390   )
391 }
392 
393 /* do a password modification nslcd call */
nslcd_request_pwmod(pam_handle_t * pamh,struct pld_cfg * cfg,const char * username,const char * service,const char * ruser,const char * rhost,const char * tty,int asroot,const char * oldpasswd,const char * newpasswd,struct nslcd_resp * resp)394 static int nslcd_request_pwmod(pam_handle_t *pamh, struct pld_cfg *cfg,
395                                const char *username, const char *service,
396                                const char *ruser, const char *rhost,
397                                const char *tty, int asroot,
398                                const char *oldpasswd, const char *newpasswd,
399                                struct nslcd_resp *resp)
400 {
401   PAM_REQUEST(
402     NSLCD_ACTION_PAM_PWMOD,
403     /* log debug message */
404     pam_syslog(pamh, LOG_DEBUG, "nslcd password modify; user=%s", username),
405     /* write the request parameters */
406     WRITE_STRING(fp, username);
407     WRITE_STRING(fp, service);
408     WRITE_STRING(fp, ruser);
409     WRITE_STRING(fp, rhost);
410     WRITE_STRING(fp, tty);
411     WRITE_INT32(fp, asroot);
412     WRITE_STRING(fp, oldpasswd);
413     WRITE_STRING(fp, newpasswd),
414     /* read the result entry */
415     READ_PAM_CODE(fp, resp->res);
416     READ_STRING(fp, resp->msg);
417   )
418 }
419 
nslcd_request_config_get(pam_handle_t * pamh,struct pld_cfg * cfg,int cfgopt,struct nslcd_resp * resp)420 static int nslcd_request_config_get(pam_handle_t *pamh, struct pld_cfg *cfg,
421                                     int cfgopt, struct nslcd_resp *resp)
422 {
423   PAM_REQUEST(
424     NSLCD_ACTION_CONFIG_GET,
425     /* log debug message */
426     pam_syslog(pamh, LOG_DEBUG, "nslcd request config (%d)", cfgopt),
427     /* write the request parameter */
428     WRITE_INT32(fp, cfgopt),
429     /* read the result entry */
430     READ_STRING(fp, resp->msg);
431   )
432 }
433 
434 /* remap the return code based on the configuration */
remap_pam_rc(int rc,struct pld_cfg * cfg)435 static int remap_pam_rc(int rc, struct pld_cfg *cfg)
436 {
437   if ((rc == PAM_AUTHINFO_UNAVAIL) && cfg->ignore_authinfo_unavail)
438     return PAM_IGNORE;
439   if ((rc == PAM_USER_UNKNOWN) && cfg->ignore_unknown_user)
440     return PAM_IGNORE;
441   return rc;
442 }
443 
444 /* PAM authentication check */
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)445 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
446                         int argc, const char **argv)
447 {
448   int rc;
449   struct pld_cfg cfg;
450   struct pld_ctx *ctx;
451   const char *username, *service;
452   const char *ruser = NULL, *rhost = NULL, *tty = NULL;
453   char *passwd = NULL;
454   struct nslcd_resp resp;
455   /* set up configuration */
456   cfg_init(pamh, flags, argc, argv, &cfg);
457   rc = init(pamh, &cfg, &ctx, &username, &service, &ruser, &rhost, &tty);
458   if (rc != PAM_SUCCESS)
459     return remap_pam_rc(rc, &cfg);
460   /* if service is "passwd" and pwdmod is not allowed alert user */
461   if (!strcmp(service, "passwd"))
462   {
463     rc = nslcd_request_config_get(pamh, &cfg, NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE,
464                                   &resp);
465     if ((rc == PAM_SUCCESS) && (resp.msg[0] != '\0'))
466     {
467       /* we silently ignore errors to get the configuration option */
468       pam_syslog(pamh, LOG_NOTICE, "password change prohibited: %s; user=%s",
469                  resp.msg, username);
470       if (!cfg.no_warn)
471         pam_error(pamh, "%s", resp.msg);
472       return remap_pam_rc(PAM_PERM_DENIED, &cfg);
473     }
474   }
475   /* prompt the user for a password */
476   rc = pam_get_authtok(pamh, PAM_AUTHTOK, (const char **)&passwd, NULL);
477   if (rc != PAM_SUCCESS)
478   {
479     pam_syslog(pamh, LOG_ERR, "failed to get password: %s",
480                pam_strerror(pamh, rc));
481     return rc;
482   }
483   /* check password */
484   if (!cfg.nullok && ((passwd == NULL) || (passwd[0] == '\0')))
485   {
486     if (cfg.debug)
487       pam_syslog(pamh, LOG_DEBUG, "user has empty password, access denied");
488     return PAM_AUTH_ERR;
489   }
490   /* do the nslcd request */
491   rc = nslcd_request_authc(pamh, &cfg, username, service, ruser, rhost, tty,
492                            passwd, &resp, &(ctx->saved_authz));
493   if (rc != PAM_SUCCESS)
494     return remap_pam_rc(rc, &cfg);
495   /* check the authentication result */
496   if (resp.res != PAM_SUCCESS)
497   {
498     pam_syslog(pamh, LOG_NOTICE, "%s; user=%s",
499                pam_strerror(pamh, resp.res), username);
500     return remap_pam_rc(resp.res, &cfg);
501   }
502   /* debug log */
503   if (cfg.debug)
504     pam_syslog(pamh, LOG_DEBUG, "authentication succeeded");
505   /* if password change is required, save old password in context */
506   if ((ctx->saved_authz.res == PAM_NEW_AUTHTOK_REQD) && (ctx->oldpassword == NULL))
507     ctx->oldpassword = strdup(passwd);
508   /* update caller's idea of the user name */
509   if ((resp.msg[0] != '\0') && (strcmp(resp.msg, username) != 0))
510   {
511     pam_syslog(pamh, LOG_INFO, "username changed from %s to %s",
512                username, resp.msg);
513     rc = pam_set_item(pamh, PAM_USER, resp.msg);
514     /* empty the username in the context to not loose our context */
515     if (ctx->username != NULL)
516     {
517       free(ctx->username);
518       ctx->username = NULL;
519     }
520   }
521   return rc;
522 }
523 
524 /* called to update the authentication credentials */
pam_sm_setcred(pam_handle_t UNUSED (* pamh),int UNUSED (flags),int UNUSED (argc),const char UNUSED (** argv))525 int pam_sm_setcred(pam_handle_t UNUSED(*pamh), int UNUSED(flags),
526                    int UNUSED(argc), const char UNUSED(**argv))
527 {
528   /* we don't need to do anything here */
529   return PAM_SUCCESS;
530 }
531 
532 /* PAM authorisation check */
pam_sm_acct_mgmt(pam_handle_t * pamh,int flags,int argc,const char ** argv)533 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
534                      int argc, const char **argv)
535 {
536   int rc;
537   struct pld_cfg cfg;
538   struct pld_ctx *ctx;
539   const char *username, *service;
540   const char *ruser = NULL, *rhost = NULL, *tty = NULL;
541   struct nslcd_resp authz_resp;
542   const char *msg = NULL;
543   /* set up configuration */
544   cfg_init(pamh, flags, argc, argv, &cfg);
545   rc = init(pamh, &cfg, &ctx, &username, &service, &ruser, &rhost, &tty);
546   if (rc != PAM_SUCCESS)
547     return remap_pam_rc(rc, &cfg);
548   /* do the nslcd request */
549   rc = nslcd_request_authz(pamh, &cfg, username, service, ruser, rhost, tty,
550                            &authz_resp);
551   if (rc != PAM_SUCCESS)
552     return remap_pam_rc(rc, &cfg);
553   /* check the returned authorisation value and the value from authentication */
554   if (authz_resp.res != PAM_SUCCESS)
555   {
556     rc = authz_resp.res;
557     msg = authz_resp.msg;
558   }
559   else if (ctx->saved_authz.res != PAM_SUCCESS)
560   {
561     rc = ctx->saved_authz.res;
562     msg = ctx->saved_authz.msg;
563   }
564   if (rc != PAM_SUCCESS)
565   {
566     /* turn in to generic PAM error message if message is empty */
567     if ((msg == NULL) || (msg[0] == '\0'))
568     {
569       msg = pam_strerror(pamh, rc);
570       pam_syslog(pamh, LOG_NOTICE, "%s; user=%s", msg, username);
571     }
572     else
573       pam_syslog(pamh, LOG_NOTICE, "%s; user=%s; err=%s",
574                  msg, username, pam_strerror(pamh, rc));
575     rc = remap_pam_rc(rc, &cfg);
576     if ((rc != PAM_IGNORE) && (!cfg.no_warn))
577       pam_error(pamh, "%s", msg);
578     return rc;
579   }
580   if (cfg.debug)
581     pam_syslog(pamh, LOG_DEBUG, "authorization succeeded");
582   /* present any informational messages to the user */
583   if ((authz_resp.msg[0] != '\0') && (!cfg.no_warn))
584   {
585     pam_info(pamh, "%s", authz_resp.msg);
586     pam_syslog(pamh, LOG_INFO, "%s; user=%s",
587                authz_resp.msg, username);
588   }
589   if ((ctx->saved_authz.msg[0] != '\0') && (!cfg.no_warn))
590   {
591     pam_info(pamh, "%s", ctx->saved_authz.msg);
592     pam_syslog(pamh, LOG_INFO, "%s; user=%s",
593                ctx->saved_authz.msg, username);
594   }
595   return PAM_SUCCESS;
596 }
597 
598 /* PAM session open call */
pam_sm_open_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)599 int pam_sm_open_session(pam_handle_t *pamh, int flags,
600                         int argc, const char **argv)
601 {
602   int rc;
603   struct pld_cfg cfg;
604   struct pld_ctx *ctx;
605   const char *username, *service;
606   const char *ruser = NULL, *rhost = NULL, *tty = NULL;
607   /* set up configuration */
608   cfg_init(pamh, flags, argc, argv, &cfg);
609   rc = init(pamh, &cfg, &ctx, &username, &service, &ruser, &rhost, &tty);
610   if (rc != PAM_SUCCESS)
611     return remap_pam_rc(rc, &cfg);
612   /* do the nslcd request */
613   rc = nslcd_request_sess_o(pamh, &cfg, username, service, ruser, rhost,
614                             tty, &(ctx->saved_session));
615   if (rc != PAM_SUCCESS)
616     return remap_pam_rc(rc, &cfg);
617   /* debug log */
618   if (cfg.debug)
619     pam_syslog(pamh, LOG_DEBUG, "session open succeeded; session_id=%s",
620                ctx->saved_session.msg);
621   return PAM_SUCCESS;
622 }
623 
624 /* PAM session close call */
pam_sm_close_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)625 int pam_sm_close_session(pam_handle_t *pamh, int flags,
626                          int argc, const char **argv)
627 {
628   int rc;
629   struct pld_cfg cfg;
630   struct pld_ctx *ctx;
631   const char *username, *service;
632   const char *ruser = NULL, *rhost = NULL, *tty = NULL;
633   /* set up configuration */
634   cfg_init(pamh, flags, argc, argv, &cfg);
635   rc = init(pamh, &cfg, &ctx, &username, &service, &ruser, &rhost, &tty);
636   if (rc != PAM_SUCCESS)
637     return remap_pam_rc(rc, &cfg);
638   /* do the nslcd request */
639   rc = nslcd_request_sess_c(pamh, &cfg, username, service, ruser, rhost,
640                             tty, ctx->saved_session.msg);
641   if (rc != PAM_SUCCESS)
642     return remap_pam_rc(rc, &cfg);
643   /* debug log */
644   if (cfg.debug)
645     pam_syslog(pamh, LOG_DEBUG, "session close succeeded; session_id=%s",
646                ctx->saved_session.msg);
647   return PAM_SUCCESS;
648 }
649 
650 /* Change the password of the user. This function is first called with
651  PAM_PRELIM_CHECK set in the flags and then without the flag. In the first
652  pass it is determined whether we can contact the LDAP server and the
653  provided old password is valid. In the second pass we get the new
654  password and actually modify the password. */
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)655 int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
656                      int argc, const char **argv)
657 {
658   int rc;
659   struct pld_cfg cfg;
660   struct pld_ctx *ctx;
661   const char *username, *service;
662   const char *ruser = NULL, *rhost = NULL, *tty = NULL;
663   const char *oldpassword = NULL, *newpassword = NULL;
664   struct passwd *pwent;
665   uid_t myuid;
666   struct nslcd_resp resp;
667   const char *msg;
668   /* set up configuration */
669   cfg_init(pamh, flags, argc, argv, &cfg);
670   rc = init(pamh, &cfg, &ctx, &username, &service, &ruser, &rhost, &tty);
671   if (rc != PAM_SUCCESS)
672     return remap_pam_rc(rc, &cfg);
673   /* check if password modification is allowed */
674   rc = nslcd_request_config_get(pamh, &cfg, NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE,
675                                 &resp);
676   if ((rc == PAM_SUCCESS) && (resp.msg[0] != '\0'))
677   {
678     /* we silently ignore errors to get the configuration option */
679     pam_syslog(pamh, LOG_NOTICE, "password change prohibited: %s; user=%s",
680                resp.msg, username);
681     if (!cfg.no_warn)
682       pam_error(pamh, "%s", resp.msg);
683     return remap_pam_rc(PAM_PERM_DENIED, &cfg);
684   }
685   /* see if we are dealing with an LDAP user first */
686   rc = nslcd_request_exists(pamh, &cfg, username);
687   if (rc != PAM_SUCCESS)
688     return remap_pam_rc(rc, &cfg);
689   /* preliminary check, just see if we can authenticate with the current password */
690   if (flags & PAM_PRELIM_CHECK)
691   {
692     ctx->asroot = 0;
693     /* see if the user is trying to modify another user's password */
694     /* TODO: perhaps this can be combined with the nslcd_request_exists() call above */
695     pwent = pam_modutil_getpwnam(args->pamh, username);
696     myuid = getuid();
697     if ((pwent != NULL) && (pwent->pw_uid != myuid) && (!(flags & PAM_CHANGE_EXPIRED_AUTHTOK)))
698     {
699       /* we are root so we can test if nslcd will allow us to change the
700          user's password without the admin password */
701       if (myuid == 0)
702       {
703         rc = nslcd_request_authc(pamh, &cfg, "", service, ruser, rhost, tty,
704                                  "", &resp, NULL);
705         if ((rc == PAM_SUCCESS) && (resp.res == PAM_SUCCESS))
706         {
707           ctx->asroot = 1;
708           return pam_set_item(pamh, PAM_OLDAUTHTOK, "");
709         }
710       }
711       /* try to  authenticate with the LDAP administrator password by passing
712          an empty username to the authc request */
713       rc = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &oldpassword,
714                            "LDAP administrator password: ");
715       if (rc != PAM_SUCCESS)
716         return rc;
717       ctx->asroot = 1;
718       username = "";
719     }
720     else if ((ctx->oldpassword != NULL) && (*ctx->oldpassword != '\0'))
721     {
722       /* we already have an old password stored (from a previous
723          authentication phase) so we'll use that and don't re-check */
724       rc = pam_set_item(pamh, PAM_OLDAUTHTOK, ctx->oldpassword);
725       return remap_pam_rc(rc, &cfg);
726     }
727     else
728     {
729       /* prompt the user for a password if needed */
730       rc = pam_get_authtok(pamh, PAM_OLDAUTHTOK, (const char **)&oldpassword,
731                            "(current) LDAP Password: ");
732       if (rc != PAM_SUCCESS)
733         return rc;
734     }
735     /* check for empty password */
736     if (!cfg.nullok && ((oldpassword == NULL) || (oldpassword[0] == '\0')))
737     {
738       if (cfg.debug)
739         pam_syslog(pamh, LOG_DEBUG, "user has empty password, access denied");
740       return PAM_AUTH_ERR;
741     }
742     /* try authenticating */
743     rc = nslcd_request_authc(pamh, &cfg, username, service, ruser, rhost,
744                              tty, oldpassword, &resp, NULL);
745     if (rc != PAM_SUCCESS)
746       return remap_pam_rc(rc, &cfg);
747     /* handle authentication result */
748     if (resp.res != PAM_SUCCESS)
749       pam_syslog(pamh, LOG_NOTICE, "%s; user=%s",
750                  pam_strerror(pamh, resp.res), username);
751     else if (cfg.debug)
752       pam_syslog(pamh, LOG_DEBUG, "authentication succeeded");
753     /* remap error code */
754     return remap_pam_rc(resp.res, &cfg);
755   }
756   /* get the old password (from the previous call) */
757   rc = pam_get_item(pamh, PAM_OLDAUTHTOK, (PAM_ITEM_CONST void **)&oldpassword);
758   if (rc != PAM_SUCCESS)
759     return rc;
760   /* prompt for new password */
761   rc = pam_get_authtok(pamh, PAM_AUTHTOK, &newpassword, NULL);
762   if (rc != PAM_SUCCESS)
763     return rc;
764   /* perform the password modification */
765   rc = nslcd_request_pwmod(pamh, &cfg, username, service, ruser, rhost, tty,
766                            ctx->asroot, oldpassword, newpassword, &resp);
767   if (rc != PAM_SUCCESS)
768     msg = pam_strerror(pamh, rc);
769   else
770   {
771     rc = resp.res;
772     msg = resp.msg;
773   }
774   /* remap error code */
775   rc = remap_pam_rc(rc, &cfg);
776   /* check the returned value */
777   if (rc != PAM_SUCCESS)
778   {
779     pam_syslog(pamh, LOG_NOTICE, "password change failed: %s; user=%s",
780                msg, username);
781     if ((rc != PAM_IGNORE) && (!cfg.no_warn))
782       pam_error(pamh, "%s", msg);
783     return rc;
784   }
785   pam_syslog(pamh, LOG_NOTICE, "password changed for %s", username);
786   return PAM_SUCCESS;
787 }
788 
789 #ifdef PAM_STATIC
790 struct pam_module PAM_NAME(modstruct) = {
791   "pam_" MODULE_NAME,
792   pam_sm_authenticate,
793   pam_sm_setcred,
794   pam_sm_acct_mgmt,
795   pam_sm_open_session,
796   pam_sm_close_session,
797   pam_sm_chauthtok
798 };
799 #endif /* PAM_STATIC */
800