Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / libpam / modules / pam_unix / unix_chkpwd.c
1 /*
2  * $Id: unix_chkpwd.c,v 1.3 2001/02/11 06:33:53 agmorgan Exp $
3  * $FreeBSD: src/contrib/libpam/modules/pam_unix/unix_chkpwd.c,v 1.1.1.1.2.2 2001/06/11 15:28:30 markm Exp $
4  *
5  * This program is designed to run setuid(root) or with sufficient
6  * privilege to read all of the unix password databases. It is designed
7  * to provide a mechanism for the current user (defined by this
8  * process' uid) to verify their own password.
9  *
10  * The password is read from the standard input. The exit status of
11  * this program indicates whether the user is authenticated or not.
12  *
13  * Copyright information is located at the end of the file.
14  *
15  */
16
17 #include <security/_pam_aconf.h>
18
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <syslog.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <pwd.h>
27 #include <shadow.h>
28 #include <signal.h>
29
30 #define MAXPASS         200     /* the maximum length of a password */
31
32 #include <security/_pam_macros.h>
33
34 #include "md5.h"
35
36 extern char *crypt(const char *key, const char *salt);
37 extern char *bigcrypt(const char *key, const char *salt);
38
39 #define UNIX_PASSED     0
40 #define UNIX_FAILED     1
41
42 /* syslogging function for errors and other information */
43
44 static void _log_err(int err, const char *format,...)
45 {
46         va_list args;
47
48         va_start(args, format);
49         openlog("unix_chkpwd", LOG_CONS | LOG_PID, LOG_AUTH);
50         vsyslog(err, format, args);
51         va_end(args);
52         closelog();
53 }
54
55 static void su_sighandler(int sig)
56 {
57         if (sig > 0) {
58                 _log_err(LOG_NOTICE, "caught signal %d.", sig);
59                 exit(sig);
60         }
61 }
62
63 static void setup_signals(void)
64 {
65         struct sigaction action;        /* posix signal structure */
66
67         /*
68          * Setup signal handlers
69          */
70         (void) memset((void *) &action, 0, sizeof(action));
71         action.sa_handler = su_sighandler;
72         action.sa_flags = SA_RESETHAND;
73         (void) sigaction(SIGILL, &action, NULL);
74         (void) sigaction(SIGTRAP, &action, NULL);
75         (void) sigaction(SIGBUS, &action, NULL);
76         (void) sigaction(SIGSEGV, &action, NULL);
77         action.sa_handler = SIG_IGN;
78         action.sa_flags = 0;
79         (void) sigaction(SIGTERM, &action, NULL);
80         (void) sigaction(SIGHUP, &action, NULL);
81         (void) sigaction(SIGINT, &action, NULL);
82         (void) sigaction(SIGQUIT, &action, NULL);
83 }
84
85 static int _unix_verify_password(const char *name, const char *p, int opt)
86 {
87         struct passwd *pwd = NULL;
88         struct spwd *spwdent = NULL;
89         char *salt = NULL;
90         char *pp = NULL;
91         int retval = UNIX_FAILED;
92
93         /* UNIX passwords area */
94         setpwent();
95         pwd = getpwnam(name);   /* Get password file entry... */
96         endpwent();
97         if (pwd != NULL) {
98                 if (strcmp(pwd->pw_passwd, "x") == 0) {
99                         /*
100                          * ...and shadow password file entry for this user,
101                          * if shadowing is enabled
102                          */
103                         setspent();
104                         spwdent = getspnam(name);
105                         endspent();
106                         if (spwdent != NULL)
107                                 salt = x_strdup(spwdent->sp_pwdp);
108                         else
109                                 pwd = NULL;
110                 } else {
111                         if (strcmp(pwd->pw_passwd, "*NP*") == 0) {      /* NIS+ */
112                                 uid_t save_uid;
113
114                                 save_uid = geteuid();
115                                 seteuid(pwd->pw_uid);
116                                 spwdent = getspnam(name);
117                                 seteuid(save_uid);
118
119                                 salt = x_strdup(spwdent->sp_pwdp);
120                         } else {
121                                 salt = x_strdup(pwd->pw_passwd);
122                         }
123                 }
124         }
125         if (pwd == NULL || salt == NULL) {
126                 _log_err(LOG_ALERT, "check pass; user unknown");
127                 p = NULL;
128                 return retval;
129         }
130
131         if (strlen(salt) == 0)
132                 return (opt == 0) ? UNIX_FAILED : UNIX_PASSED;
133
134         /* the moment of truth -- do we agree with the password? */
135         retval = UNIX_FAILED;
136         if (!strncmp(salt, "$1$", 3)) {
137                 pp = Goodcrypt_md5(p, salt);
138                 if (strcmp(pp, salt) == 0) {
139                         retval = UNIX_PASSED;
140                 } else {
141                         pp = Brokencrypt_md5(p, salt);
142                         if (strcmp(pp, salt) == 0)
143                                 retval = UNIX_PASSED;
144                 }
145         } else {
146                 pp = bigcrypt(p, salt);
147                 if (strcmp(pp, salt) == 0) {
148                         retval = UNIX_PASSED;
149                 }
150         }
151         p = NULL;               /* no longer needed here */
152
153         /* clean up */
154         {
155                 char *tp = pp;
156                 if (pp != NULL) {
157                         while (tp && *tp)
158                                 *tp++ = '\0';
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  */