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