]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/unix_chkpwd.c
Relevant BUGIDs: 2487654
[linux-pam] / modules / pam_unix / unix_chkpwd.c
1 /*
2  * This program is designed to run setuid(root) or with sufficient
3  * privilege to read all of the unix password databases. It is designed
4  * to provide a mechanism for the current user (defined by this
5  * process' uid) to verify their own password.
6  *
7  * The password is read from the standard input. The exit status of
8  * this program indicates whether the user is authenticated or not.
9  *
10  * Copyright information is located at the end of the file.
11  *
12  */
13
14 #include "config.h"
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <syslog.h>
20 #include <unistd.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <pwd.h>
24 #include <shadow.h>
25 #include <signal.h>
26 #include <time.h>
27 #include <errno.h>
28 #ifdef HAVE_LIBAUDIT
29 #include <libaudit.h>
30 #endif
31
32 #include <security/_pam_types.h>
33 #include <security/_pam_macros.h>
34
35 #include "passverify.h"
36
37 static int _check_expiry(const char *uname)
38 {
39         struct spwd *spent;
40         struct passwd *pwent;
41         int retval;
42         int daysleft;
43
44         retval = get_account_info(uname, &pwent, &spent);
45         if (retval != PAM_SUCCESS) {
46                 helper_log_err(LOG_ALERT, "could not obtain user info (%s)", uname);
47                 printf("-1\n");
48                 return retval;
49         }
50
51         if (spent == NULL) {
52                 printf("-1\n");
53                 return retval;
54         }
55
56         retval = check_shadow_expiry(spent, &daysleft);
57         printf("%d\n", daysleft);
58         return retval;
59 }
60
61 #ifdef HAVE_LIBAUDIT
62 static int _audit_log(int type, const char *uname, int rc)
63 {
64         int audit_fd;
65
66         audit_fd = audit_open();
67         if (audit_fd < 0) {
68                 /* You get these error codes only when the kernel doesn't have
69                  * audit compiled in. */
70                 if (errno == EINVAL || errno == EPROTONOSUPPORT ||
71                         errno == EAFNOSUPPORT)
72                         return PAM_SUCCESS;
73
74                 helper_log_err(LOG_CRIT, "audit_open() failed: %m");
75                 return PAM_AUTH_ERR;
76         }
77
78         rc = audit_log_acct_message(audit_fd, type, NULL, "PAM:unix_chkpwd",
79                 uname, -1, NULL, NULL, NULL, rc == PAM_SUCCESS);
80         if (rc == -EPERM && geteuid() != 0) {
81                 rc = 0;
82         }
83
84         audit_close(audit_fd);
85
86         return rc < 0 ? PAM_AUTH_ERR : PAM_SUCCESS;
87 }
88 #endif
89
90 int main(int argc, char *argv[])
91 {
92         char pass[MAXPASS + 1];
93         char *option;
94         int npass, nullok;
95         int blankpass = 0;
96         int retval = PAM_AUTH_ERR;
97         char *user;
98         char *passwords[] = { pass };
99
100         /*
101          * Catch or ignore as many signal as possible.
102          */
103         setup_signals();
104
105         /*
106          * we establish that this program is running with non-tty stdin.
107          * this is to discourage casual use. It does *NOT* prevent an
108          * intruder from repeatadly running this program to determine the
109          * password of the current user (brute force attack, but one for
110          * which the attacker must already have gained access to the user's
111          * account).
112          */
113
114         if (isatty(STDIN_FILENO) || argc != 3 ) {
115                 helper_log_err(LOG_NOTICE
116                       ,"inappropriate use of Unix helper binary [UID=%d]"
117                          ,getuid());
118 #ifdef HAVE_LIBAUDIT
119                 _audit_log(AUDIT_ANOM_EXEC, getuidname(getuid()), PAM_SYSTEM_ERR);
120 #endif
121                 fprintf(stderr
122                  ,"This binary is not designed for running in this way\n"
123                       "-- the system administrator has been informed\n");
124                 sleep(10);      /* this should discourage/annoy the user */
125                 return PAM_SYSTEM_ERR;
126         }
127
128         /*
129          * Determine what the current user's name is.
130          * We must thus skip the check if the real uid is 0.
131          */
132         if (getuid() == 0) {
133           user=argv[1];
134         }
135         else {
136           user = getuidname(getuid());
137           /* if the caller specifies the username, verify that user
138              matches it */
139           if (strcmp(user, argv[1])) {
140             user = argv[1];
141             /* no match -> permanently change to the real user and proceed */
142             if (setuid(getuid()) != 0)
143                 return PAM_AUTH_ERR;
144           }
145         }
146
147         option=argv[2];
148
149         if (strcmp(option, "chkexpiry") == 0)
150           /* Check account information from the shadow file */
151           return _check_expiry(argv[1]);
152         /* read the nullok/nonull option */
153         else if (strcmp(option, "nullok") == 0)
154           nullok = 1;
155         else if (strcmp(option, "nonull") == 0)
156           nullok = 0;
157         else {
158 #ifdef HAVE_LIBAUDIT
159           _audit_log(AUDIT_ANOM_EXEC, getuidname(getuid()), PAM_SYSTEM_ERR);
160 #endif
161           return PAM_SYSTEM_ERR;
162         }
163         /* read the password from stdin (a pipe from the pam_unix module) */
164
165         npass = read_passwords(STDIN_FILENO, 1, passwords);
166
167         if (npass != 1) {       /* is it a valid password? */
168                 helper_log_err(LOG_DEBUG, "no password supplied");
169                 *pass = '\0';
170         }
171
172         if (*pass == '\0') {
173                 blankpass = 1;
174         }
175
176         retval = helper_verify_password(user, pass, nullok);
177
178         memset(pass, '\0', MAXPASS);    /* clear memory of the password */
179
180         /* return pass or fail */
181
182         if (retval != PAM_SUCCESS) {
183                 if (!nullok || !blankpass) {
184                         /* no need to log blank pass test */
185 #ifdef HAVE_LIBAUDIT
186                         if (getuid() != 0)
187                                 _audit_log(AUDIT_USER_AUTH, user, PAM_AUTH_ERR);
188 #endif
189                         helper_log_err(LOG_NOTICE, "password check failed for user (%s)", user);
190                 }
191                 return PAM_AUTH_ERR;
192         } else {
193                 if (getuid() != 0) {
194 #ifdef HAVE_LIBAUDIT
195                         return _audit_log(AUDIT_USER_AUTH, user, PAM_SUCCESS);
196 #else
197                         return PAM_SUCCESS;
198 #endif
199                 }
200                 return PAM_SUCCESS;
201         }
202 }
203
204 /*
205  * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
206  * Copyright (c) Red Hat, Inc., 2007,2008. All rights reserved
207  *
208  * Redistribution and use in source and binary forms, with or without
209  * modification, are permitted provided that the following conditions
210  * are met:
211  * 1. Redistributions of source code must retain the above copyright
212  *    notice, and the entire permission notice in its entirety,
213  *    including the disclaimer of warranties.
214  * 2. Redistributions in binary form must reproduce the above copyright
215  *    notice, this list of conditions and the following disclaimer in the
216  *    documentation and/or other materials provided with the distribution.
217  * 3. The name of the author may not be used to endorse or promote
218  *    products derived from this software without specific prior
219  *    written permission.
220  *
221  * ALTERNATIVELY, this product may be distributed under the terms of
222  * the GNU Public License, in which case the provisions of the GPL are
223  * required INSTEAD OF the above restrictions.  (This clause is
224  * necessary due to a potential bad interaction between the GPL and
225  * the restrictions contained in a BSD-style copyright.)
226  *
227  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
228  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
229  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
230  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
231  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
232  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
233  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
234  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
235  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
236  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
237  * OF THE POSSIBILITY OF SUCH DAMAGE.
238  */