]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/unix_chkpwd.c
Relevant BUGIDs: task 15788, bugs 108297, 117476, 117474
[linux-pam] / modules / pam_unix / unix_chkpwd.c
1 /*
2  * $Id$
3  *
4  * This program is designed to run setuid(root) or with sufficient
5  * privilege to read all of the unix password databases. It is designed
6  * to provide a mechanism for the current user (defined by this
7  * process' uid) to verify their own password.
8  *
9  * The password is read from the standard input. The exit status of
10  * this program indicates whether the user is authenticated or not.
11  *
12  * Copyright information is located at the end of the file.
13  *
14  */
15
16 #include <security/_pam_aconf.h>
17
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <syslog.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <pwd.h>
26 #include <shadow.h>
27 #include <signal.h>
28
29 #define MAXPASS         200     /* the maximum length of a password */
30
31 #include <security/_pam_macros.h>
32
33 #include "md5.h"
34
35 extern char *crypt(const char *key, const char *salt);
36 extern char *bigcrypt(const char *key, const char *salt);
37
38 #define UNIX_PASSED     0
39 #define UNIX_FAILED     1
40
41 /* syslogging function for errors and other information */
42
43 static void _log_err(int err, const char *format,...)
44 {
45         va_list args;
46
47         va_start(args, format);
48         openlog("unix_chkpwd", LOG_CONS | LOG_PID, LOG_AUTH);
49         vsyslog(err, format, args);
50         va_end(args);
51         closelog();
52 }
53
54 static void su_sighandler(int sig)
55 {
56         if (sig > 0) {
57                 _log_err(LOG_NOTICE, "caught signal %d.", sig);
58                 exit(sig);
59         }
60 }
61
62 static void setup_signals(void)
63 {
64         struct sigaction action;        /* posix signal structure */
65
66         /*
67          * Setup signal handlers
68          */
69         (void) memset((void *) &action, 0, sizeof(action));
70         action.sa_handler = su_sighandler;
71         action.sa_flags = SA_RESETHAND;
72         (void) sigaction(SIGILL, &action, NULL);
73         (void) sigaction(SIGTRAP, &action, NULL);
74         (void) sigaction(SIGBUS, &action, NULL);
75         (void) sigaction(SIGSEGV, &action, NULL);
76         action.sa_handler = SIG_IGN;
77         action.sa_flags = 0;
78         (void) sigaction(SIGTERM, &action, NULL);
79         (void) sigaction(SIGHUP, &action, NULL);
80         (void) sigaction(SIGINT, &action, NULL);
81         (void) sigaction(SIGQUIT, &action, NULL);
82 }
83
84 static int _unix_verify_password(const char *name, const char *p, int opt)
85 {
86         struct passwd *pwd = NULL;
87         struct spwd *spwdent = NULL;
88         char *salt = NULL;
89         char *pp = NULL;
90         int retval = UNIX_FAILED;
91
92         /* UNIX passwords area */
93         setpwent();
94         pwd = getpwnam(name);   /* Get password file entry... */
95         endpwent();
96         if (pwd != NULL) {
97                 if (strcmp(pwd->pw_passwd, "x") == 0) {
98                         /*
99                          * ...and shadow password file entry for this user,
100                          * if shadowing is enabled
101                          */
102                         setspent();
103                         spwdent = getspnam(name);
104                         endspent();
105                         if (spwdent != NULL)
106                                 salt = x_strdup(spwdent->sp_pwdp);
107                         else
108                                 pwd = NULL;
109                 } else {
110                         if (strcmp(pwd->pw_passwd, "*NP*") == 0) {      /* NIS+ */
111                                 uid_t save_uid;
112
113                                 save_uid = geteuid();
114                                 seteuid(pwd->pw_uid);
115                                 spwdent = getspnam(name);
116                                 seteuid(save_uid);
117
118                                 salt = x_strdup(spwdent->sp_pwdp);
119                         } else {
120                                 salt = x_strdup(pwd->pw_passwd);
121                         }
122                 }
123         }
124         if (pwd == NULL || salt == NULL) {
125                 _log_err(LOG_ALERT, "check pass; user unknown");
126                 p = NULL;
127                 return retval;
128         }
129
130         if (strlen(salt) == 0)
131                 return (opt == 0) ? UNIX_FAILED : UNIX_PASSED;
132
133         /* the moment of truth -- do we agree with the password? */
134         retval = UNIX_FAILED;
135         if (!strncmp(salt, "$1$", 3)) {
136                 pp = Goodcrypt_md5(p, salt);
137                 if (strcmp(pp, salt) == 0) {
138                         retval = UNIX_PASSED;
139                 } else {
140                         pp = Brokencrypt_md5(p, salt);
141                         if (strcmp(pp, salt) == 0)
142                                 retval = UNIX_PASSED;
143                 }
144         } else {
145                 pp = bigcrypt(p, salt);
146                 if (strcmp(pp, salt) == 0) {
147                         retval = UNIX_PASSED;
148                 }
149         }
150         p = NULL;               /* no longer needed here */
151
152         /* clean up */
153         {
154                 char *tp = pp;
155                 if (pp != NULL) {
156                         while (tp && *tp)
157                                 *tp++ = '\0';
158                 }
159                 pp = tp = NULL;
160         }
161
162         return retval;
163 }
164
165 static char *getuidname(uid_t uid)
166 {
167         struct passwd *pw;
168 #if 0
169         char *envname;
170
171         envname = getenv("LOGNAME");
172         if (envname == NULL)
173                 return NULL;
174
175         pw = getpwuid(uid);
176         if (pw == NULL)
177                 return NULL;
178
179         if (strcmp(envname, pw->pw_name))
180                 return NULL;
181
182         return envname;
183 #else
184         static char username[32];
185
186         pw = getpwuid(uid);
187         if (pw == NULL)
188                 return NULL;
189
190         memset(username, 0, 32);
191         strncpy(username, pw->pw_name, 32);
192         username[31] = '\0';
193         
194         return username;
195 #endif
196 }
197
198 int main(int argc, char *argv[])
199 {
200         char pass[MAXPASS + 1];
201         char option[8];
202         int npass, opt;
203         int retval = UNIX_FAILED;
204         char *user;
205
206         /*
207          * Catch or ignore as many signal as possible.
208          */
209         setup_signals();
210
211         /*
212          * we establish that this program is running with non-tty stdin.
213          * this is to discourage casual use. It does *NOT* prevent an
214          * intruder from repeatadly running this program to determine the
215          * password of the current user (brute force attack, but one for
216          * which the attacker must already have gained access to the user's
217          * account).
218          */
219
220         if (isatty(STDIN_FILENO)) {
221
222                 _log_err(LOG_NOTICE
223                       ,"inappropriate use of Unix helper binary [UID=%d]"
224                          ,getuid());
225                 fprintf(stderr
226                  ,"This binary is not designed for running in this way\n"
227                       "-- the system administrator has been informed\n");
228                 sleep(10);      /* this should discourage/annoy the user */
229                 return UNIX_FAILED;
230         }
231         /*
232          * determine the current user's name is
233          * 1. supplied as a environment variable as LOGNAME
234          * 2. the uid has to match the one associated with the LOGNAME.
235          */
236         user = getuidname(getuid());
237
238         /* read the nollok/nonull option */
239
240         npass = read(STDIN_FILENO, option, 8);
241
242         if (npass < 0) {
243                 _log_err(LOG_DEBUG, "no option supplied");
244                 return UNIX_FAILED;
245         } else {
246                 option[7] = '\0';
247                 if (strncmp(option, "nullok", 8) == 0)
248                         opt = 1;
249                 else
250                         opt = 0;
251         }
252
253         /* read the password from stdin (a pipe from the pam_unix module) */
254
255         npass = read(STDIN_FILENO, pass, MAXPASS);
256
257         if (npass < 0) {        /* is it a valid password? */
258
259                 _log_err(LOG_DEBUG, "no password supplied");
260
261         } else if (npass >= MAXPASS) {
262
263                 _log_err(LOG_DEBUG, "password too long");
264
265         } else {
266                 if (npass == 0) {
267                         /* the password is NULL */
268
269                         retval = _unix_verify_password(user, NULL, opt);
270
271                 } else {
272                         /* does pass agree with the official one? */
273
274                         pass[npass] = '\0';     /* NUL terminate */
275                         retval = _unix_verify_password(user, pass, opt);
276
277                 }
278         }
279
280         memset(pass, '\0', MAXPASS);    /* clear memory of the password */
281
282         /* return pass or fail */
283
284         return retval;
285 }
286
287 /*
288  * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
289  *
290  * Redistribution and use in source and binary forms, with or without
291  * modification, are permitted provided that the following conditions
292  * are met:
293  * 1. Redistributions of source code must retain the above copyright
294  *    notice, and the entire permission notice in its entirety,
295  *    including the disclaimer of warranties.
296  * 2. Redistributions in binary form must reproduce the above copyright
297  *    notice, this list of conditions and the following disclaimer in the
298  *    documentation and/or other materials provided with the distribution.
299  * 3. The name of the author may not be used to endorse or promote
300  *    products derived from this software without specific prior
301  *    written permission.
302  * 
303  * ALTERNATIVELY, this product may be distributed under the terms of
304  * the GNU Public License, in which case the provisions of the GPL are
305  * required INSTEAD OF the above restrictions.  (This clause is
306  * necessary due to a potential bad interaction between the GPL and
307  * the restrictions contained in a BSD-style copyright.)
308  * 
309  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
310  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
311  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
312  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
313  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
314  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
315  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
316  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
317  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
318  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
319  * OF THE POSSIBILITY OF SUCH DAMAGE.
320  */