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