]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/pam_unix_acct.c
Relevant BUGIDs:
[linux-pam] / modules / pam_unix / pam_unix_acct.c
1 /*
2  * Copyright Elliot Lee, 1996.  All rights reserved.
3  * Copyright Jan Rêkorajski, 1999.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, and the entire permission notice in its entirety,
10  *    including the disclaimer of warranties.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote
15  *    products derived from this software without specific prior
16  *    written permission.
17  *
18  * ALTERNATIVELY, this product may be distributed under the terms of
19  * the GNU Public License, in which case the provisions of the GPL are
20  * required INSTEAD OF the above restrictions.  (This clause is
21  * necessary due to a potential bad interaction between the GPL and
22  * the restrictions contained in a BSD-style copyright.)
23  *
24  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
25  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
28  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34  * OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36
37 #include "config.h"
38
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <sys/types.h>
44 #include <syslog.h>
45 #include <pwd.h>
46 #include <shadow.h>
47 #include <time.h>               /* for time() */
48 #include <errno.h>
49 #include <sys/wait.h>
50 #ifdef WITH_SELINUX
51 #include <selinux/selinux.h>
52 #define SELINUX_ENABLED is_selinux_enabled()>0
53 #endif
54
55 #include <security/_pam_macros.h>
56
57 /* indicate that the following groups are defined */
58
59 #define PAM_SM_ACCOUNT
60
61 #include <security/pam_modules.h>
62 #include <security/pam_ext.h>
63 #include <security/pam_modutil.h>
64
65 #include "support.h"
66
67 #ifdef WITH_SELINUX
68
69 struct spwd spwd;
70
71 struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user)
72 {
73   int retval=0, child, fds[2];
74   void (*sighandler)(int) = NULL;
75   D(("running verify_binary"));
76
77   /* create a pipe for the messages */
78   if (pipe(fds) != 0) {
79     D(("could not make pipe"));
80     pam_syslog(pamh, LOG_ERR, "Could not make pipe: %m");
81     return NULL;
82   }
83   D(("called."));
84
85   if (off(UNIX_NOREAP, ctrl)) {
86     /*
87      * This code arranges that the demise of the child does not cause
88      * the application to receive a signal it is not expecting - which
89      * may kill the application or worse.
90      *
91      * The "noreap" module argument is provided so that the admin can
92      * override this behavior.
93      */
94     sighandler = signal(SIGCHLD, SIG_DFL);
95   }
96
97   /* fork */
98   child = fork();
99   if (child == 0) {
100     size_t i=0;
101     struct rlimit rlim;
102     static char *envp[] = { NULL };
103     char *args[] = { NULL, NULL, NULL, NULL };
104
105     close(0); close(1);
106     /* reopen stdin as pipe */
107     close(fds[0]);
108     dup2(fds[1], STDOUT_FILENO);
109
110     /* XXX - should really tidy up PAM here too */
111
112     if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
113       for (i=2; i < rlim.rlim_max; i++) {
114         if ((unsigned int)fds[1] != i) {
115           close(i);
116         }
117       }
118     }
119     /* exec binary helper */
120     args[0] = x_strdup(CHKPWD_HELPER);
121     args[1] = x_strdup(user);
122     args[2] = x_strdup("verify");
123
124     execve(CHKPWD_HELPER, args, envp);
125
126     pam_syslog(pamh, LOG_ERR, "helper binary execve failed: %m");
127     /* should not get here: exit with error */
128     close (fds[1]);
129     D(("helper binary is not available"));
130     exit(PAM_AUTHINFO_UNAVAIL);
131   } else {
132     close(fds[1]);
133     if (child > 0) {
134       char buf[1024];
135       int rc=0;
136       rc=waitpid(child, &retval, 0);  /* wait for helper to complete */
137       if (rc<0) {
138         pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
139         retval = PAM_AUTH_ERR;
140       } else {
141         retval = WEXITSTATUS(retval);
142         if (retval != PAM_AUTHINFO_UNAVAIL) {
143           rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1);
144           if(rc > 0) {
145               buf[rc] = '\0';
146               if (sscanf(buf,"%ld:%ld:%ld:%ld:%ld:%ld",
147                      &spwd.sp_lstchg, /* last password change */
148                      &spwd.sp_min, /* days until change allowed. */
149                      &spwd.sp_max, /* days before change required */
150                      &spwd.sp_warn, /* days warning for expiration */
151                      &spwd.sp_inact, /* days before account inactive */
152                      &spwd.sp_expire) /* date when account expires */ != 6 ) retval = PAM_AUTH_ERR;
153             }
154           else {
155             pam_syslog(pamh, LOG_ERR, " ERROR %d: %m", rc); retval = PAM_AUTH_ERR;
156           }
157         }
158       }
159     } else {
160       pam_syslog(pamh, LOG_ERR, "Fork failed: %m");
161       D(("fork failed"));
162       retval = PAM_AUTH_ERR;
163     }
164     close(fds[0]);
165   }
166   if (sighandler != NULL) {
167     (void) signal(SIGCHLD, sighandler);   /* restore old signal handler */
168   }
169   D(("Returning %d",retval));
170   if (retval != PAM_SUCCESS) {
171     return NULL;
172   }
173   return &spwd;
174 }
175
176 #endif
177
178
179 /*
180  * PAM framework looks for this entry-point to pass control to the
181  * account management module.
182  */
183
184 PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags,
185                                 int argc, const char **argv)
186 {
187         unsigned int ctrl;
188         const void *void_uname;
189         const char *uname;
190         int retval, daysleft;
191         time_t curdays;
192         struct spwd *spent;
193         struct passwd *pwent;
194         char buf[256];
195
196         D(("called."));
197
198         ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
199
200         retval = pam_get_item(pamh, PAM_USER, &void_uname);
201         uname = void_uname;
202         D(("user = `%s'", uname));
203         if (retval != PAM_SUCCESS || uname == NULL) {
204                 pam_syslog(pamh, LOG_ALERT,
205                          "could not identify user (from uid=%d)",
206                          getuid());
207                 return PAM_USER_UNKNOWN;
208         }
209
210         pwent = pam_modutil_getpwnam(pamh, uname);
211         if (!pwent) {
212                 pam_syslog(pamh, LOG_ALERT,
213                          "could not identify user (from getpwnam(%s))",
214                          uname);
215                 return PAM_USER_UNKNOWN;
216         }
217
218         if (!strcmp( pwent->pw_passwd, "*NP*" )) { /* NIS+ */
219                 uid_t save_euid, save_uid;
220
221                 save_euid = geteuid();
222                 save_uid = getuid();
223                 if (save_uid == pwent->pw_uid)
224                         setreuid( save_euid, save_uid );
225                 else  {
226                         setreuid( 0, -1 );
227                         if (setreuid( -1, pwent->pw_uid ) == -1) {
228                                 setreuid( -1, 0 );
229                                 setreuid( 0, -1 );
230                                 if(setreuid( -1, pwent->pw_uid ) == -1)
231                                         return PAM_CRED_INSUFFICIENT;
232                         }
233                 }
234                 spent = pam_modutil_getspnam (pamh, uname);
235                 if (save_uid == pwent->pw_uid)
236                         setreuid( save_uid, save_euid );
237                 else {
238                         if (setreuid( -1, 0 ) == -1)
239                         setreuid( save_uid, -1 );
240                         setreuid( -1, save_euid );
241                 }
242
243         } else if (_unix_shadowed (pwent))
244                 spent = pam_modutil_getspnam (pamh, uname);
245         else
246                 return PAM_SUCCESS;
247
248 #ifdef WITH_SELINUX
249         if (!spent && SELINUX_ENABLED )
250             spent = _unix_run_verify_binary(pamh, ctrl, uname);
251 #endif
252
253         if (!spent)
254                 if (on(UNIX_BROKEN_SHADOW,ctrl))
255                         return PAM_SUCCESS;
256
257         if (!spent)
258                 return PAM_AUTHINFO_UNAVAIL;    /* Couldn't get username from shadow */
259
260         curdays = time(NULL) / (60 * 60 * 24);
261         D(("today is %d, last change %d", curdays, spent->sp_lstchg));
262         if ((curdays > spent->sp_expire) && (spent->sp_expire != -1)) {
263                 pam_syslog(pamh, LOG_NOTICE,
264                          "account %s has expired (account expired)",
265                          uname);
266                 _make_remark(pamh, ctrl, PAM_ERROR_MSG,
267                              _("Your account has expired; please contact your system administrator"));
268                 D(("account expired"));
269                 return PAM_ACCT_EXPIRED;
270         }
271         if (spent->sp_lstchg == 0) {
272                 pam_syslog(pamh, LOG_NOTICE,
273                          "expired password for user %s (root enforced)",
274                          uname);
275                 _make_remark(pamh, ctrl, PAM_ERROR_MSG,
276                              _("You are required to change your password immediately (root enforced)"));
277                 D(("need a new password"));
278                 return PAM_NEW_AUTHTOK_REQD;
279         }
280         if (curdays < spent->sp_lstchg) {
281                 pam_syslog(pamh, LOG_DEBUG,
282                          "account %s has password changed in future",
283                          uname);
284                 return PAM_SUCCESS;
285         }
286         if ((curdays - spent->sp_lstchg > spent->sp_max)
287             && (curdays - spent->sp_lstchg > spent->sp_inact)
288             && (curdays - spent->sp_lstchg > spent->sp_max + spent->sp_inact)
289             && (spent->sp_max != -1) && (spent->sp_inact != -1)) {
290                 pam_syslog(pamh, LOG_NOTICE,
291                     "account %s has expired (failed to change password)",
292                     uname);
293                 _make_remark(pamh, ctrl, PAM_ERROR_MSG,
294                              _("Your account has expired; please contact your system administrator"));
295                 D(("account expired 2"));
296                 return PAM_ACCT_EXPIRED;
297         }
298         if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) {
299                 pam_syslog(pamh, LOG_DEBUG,
300                          "expired password for user %s (password aged)",
301                          uname);
302                 _make_remark(pamh, ctrl, PAM_ERROR_MSG,
303                              _("You are required to change your password immediately (password aged)"));
304                 D(("need a new password 2"));
305                 return PAM_NEW_AUTHTOK_REQD;
306         }
307         if ((curdays - spent->sp_lstchg > spent->sp_max - spent->sp_warn)
308             && (spent->sp_max != -1) && (spent->sp_warn != -1)) {
309                 daysleft = (spent->sp_lstchg + spent->sp_max) - curdays;
310                 pam_syslog(pamh, LOG_DEBUG,
311                          "password for user %s will expire in %d days",
312                          uname, daysleft);
313                 snprintf(buf, sizeof (buf), _("Warning: your password will expire in %d day%.2s"),
314                          daysleft, daysleft == 1 ? "" : "s");
315                 _make_remark(pamh, ctrl, PAM_TEXT_INFO, buf);
316         }
317
318         D(("all done"));
319
320         return PAM_SUCCESS;
321 }
322
323
324 /* static module data */
325 #ifdef PAM_STATIC
326 struct pam_module _pam_unix_acct_modstruct = {
327     "pam_unix_acct",
328     NULL,
329     NULL,
330     pam_sm_acct_mgmt,
331     NULL,
332     NULL,
333     NULL,
334 };
335 #endif