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