]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/unix_chkpwd.c
Relevant BUGIDs: 440107
[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                         free(pp);
159                 }
160                 pp = tp = NULL;
161         }
162
163         return retval;
164 }
165
166 static char *getuidname(uid_t uid)
167 {
168         struct passwd *pw;
169         static char username[32];
170
171         pw = getpwuid(uid);
172         if (pw == NULL)
173                 return NULL;
174
175         memset(username, 0, 32);
176         strncpy(username, pw->pw_name, 32);
177         username[31] = '\0';
178         
179         return username;
180 }
181
182 int main(int argc, char *argv[])
183 {
184         char pass[MAXPASS + 1];
185         char option[8];
186         int npass, opt;
187         int force_failure = 0;
188         int retval = UNIX_FAILED;
189         char *user;
190
191         /*
192          * Catch or ignore as many signal as possible.
193          */
194         setup_signals();
195
196         /*
197          * we establish that this program is running with non-tty stdin.
198          * this is to discourage casual use. It does *NOT* prevent an
199          * intruder from repeatadly running this program to determine the
200          * password of the current user (brute force attack, but one for
201          * which the attacker must already have gained access to the user's
202          * account).
203          */
204
205         if (isatty(STDIN_FILENO)) {
206
207                 _log_err(LOG_NOTICE
208                       ,"inappropriate use of Unix helper binary [UID=%d]"
209                          ,getuid());
210                 fprintf(stderr
211                  ,"This binary is not designed for running in this way\n"
212                       "-- the system administrator has been informed\n");
213                 sleep(10);      /* this should discourage/annoy the user */
214                 return UNIX_FAILED;
215         }
216
217         /*
218          * determine the current user's name is
219          */
220         user = getuidname(getuid());
221         if (argc == 2) {
222             /* if the caller specifies the username, verify that user
223                matches it */
224             if (strcmp(user, argv[1])) {
225                 force_failure = 1;
226             }
227         }
228
229         /* read the nollok/nonull option */
230
231         npass = read(STDIN_FILENO, option, 8);
232
233         if (npass < 0) {
234                 _log_err(LOG_DEBUG, "no option supplied");
235                 return UNIX_FAILED;
236         } else {
237                 option[7] = '\0';
238                 if (strncmp(option, "nullok", 8) == 0)
239                         opt = 1;
240                 else
241                         opt = 0;
242         }
243
244         /* read the password from stdin (a pipe from the pam_unix module) */
245
246         npass = read(STDIN_FILENO, pass, MAXPASS);
247
248         if (npass < 0) {        /* is it a valid password? */
249
250                 _log_err(LOG_DEBUG, "no password supplied");
251
252         } else if (npass >= MAXPASS) {
253
254                 _log_err(LOG_DEBUG, "password too long");
255
256         } else {
257                 if (npass == 0) {
258                         /* the password is NULL */
259
260                         retval = _unix_verify_password(user, NULL, opt);
261
262                 } else {
263                         /* does pass agree with the official one? */
264
265                         pass[npass] = '\0';     /* NUL terminate */
266                         retval = _unix_verify_password(user, pass, opt);
267
268                 }
269         }
270
271         memset(pass, '\0', MAXPASS);    /* clear memory of the password */
272
273         /* return pass or fail */
274
275         if ((retval != UNIX_PASSED) || force_failure) {
276             return UNIX_FAILED;
277         } else {
278             return UNIX_PASSED;
279         }
280 }
281
282 /*
283  * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
284  *
285  * Redistribution and use in source and binary forms, with or without
286  * modification, are permitted provided that the following conditions
287  * are met:
288  * 1. Redistributions of source code must retain the above copyright
289  *    notice, and the entire permission notice in its entirety,
290  *    including the disclaimer of warranties.
291  * 2. Redistributions in binary form must reproduce the above copyright
292  *    notice, this list of conditions and the following disclaimer in the
293  *    documentation and/or other materials provided with the distribution.
294  * 3. The name of the author may not be used to endorse or promote
295  *    products derived from this software without specific prior
296  *    written permission.
297  * 
298  * ALTERNATIVELY, this product may be distributed under the terms of
299  * the GNU Public License, in which case the provisions of the GPL are
300  * required INSTEAD OF the above restrictions.  (This clause is
301  * necessary due to a potential bad interaction between the GPL and
302  * the restrictions contained in a BSD-style copyright.)
303  * 
304  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
305  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
306  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
307  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
308  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
309  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
310  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
312  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
313  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
314  * OF THE POSSIBILITY OF SUCH DAMAGE.
315  */