Initial commit for Tizen
[profile/extras/shadow-utils.git] / contrib / pwdauth.c
1 /*
2  * pwdauth.c - program to verify a given username/password pair.
3  *
4  * Run it with username in argv[1] (may be omitted - default is the
5  * current user), and send it the password over a pipe on stdin.
6  * Exit status: 0 - correct password, 1 - wrong password, >1 - other
7  * errors.  For use with shadow passwords, this program should be
8  * installed setuid root.
9  *
10  * This can be used, for example, by xlock - you don't have to install
11  * this large and complex (== possibly insecure) program setuid root,
12  * just modify it to run this simple program to do the authentication.
13  *
14  * Recent versions (xlockmore-3.9) are cleaner, and drop privileges as
15  * soon as possible after getting the user's encrypted password.
16  * Using this program probably doesn't make it more secure, and has one
17  * disadvantage: since we don't get the encrypted user's password at
18  * startup (but at the time the user is authenticated), it is not clear
19  * how we should handle errors (like getpwnam() returning NULL).
20  * - fail the authentication?  Problem: no way to unlock (other than kill
21  *   the process from somewhere else) if the NIS server stops responding.
22  * - succeed and unlock?  Problem: it's too easy to unlock by unplugging
23  *   the box from the network and waiting until NIS times out...
24  *
25  * This program is Copyright (C) 1996 Marek Michalkiewicz
26  * <marekm@i17linuxb.ists.pwr.wroc.pl>.
27  *
28  * It may be used and distributed freely for any purposes.  There is no
29  * warranty - use at your own risk.  I am not liable for any damages etc.
30  * If you improve it, please send me your changes.
31  */
32
33 static char rcsid[] = "$Id: pwdauth.c 1342 2007-11-10 23:46:11Z nekral-guest $";
34
35 /*
36  * Define USE_SYSLOG to use syslog() to log successful and failed
37  * authentication.  This should be safe even if your system has
38  * the infamous syslog buffer overrun security problem...
39  */
40 #define USE_SYSLOG
41
42 /*
43  * Define HAVE_GETSPNAM to get shadow passwords using getspnam().
44  * Some systems don't have getspnam(), but getpwnam() returns
45  * encrypted passwords only if running as root.
46  *
47  * According to the xlock source (not tested, except Linux) -
48  * define: Linux, Solaris 2.x, SVR4, ...
49  * undef: HP-UX with Secured Passwords, FreeBSD, NetBSD, QNX.
50  * Known not supported (yet): Ultrix, OSF/1, SCO.
51  */
52 #define HAVE_GETSPNAM
53
54 /*
55  * Define HAVE_PW_ENCRYPT to use pw_encrypt() instead of crypt().
56  * pw_encrypt() is like the standard crypt(), except that it may
57  * support better password hashing algorithms.
58  *
59  * Define if linking with libshadow.a from the shadow password
60  * suite (Linux, SunOS 4.x?).
61  */
62 #undef HAVE_PW_ENCRYPT
63
64 /*
65  * Define HAVE_AUTH_METHODS to support the shadow suite specific
66  * extension: the encrypted password field contains a list of
67  * administrator defined authentication methods, separated by
68  * semicolons.  This program only supports the standard password
69  * authentication method (a string that doesn't start with '@').
70  */
71 #undef HAVE_AUTH_METHODS
72
73 /*
74  * FAIL_DELAY - number of seconds to sleep before exiting if the
75  * password was wrong, to slow down password guessing attempts.
76  */
77 #define FAIL_DELAY 2
78
79 /* No user-serviceable parts below :-).  */
80
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <sys/types.h>
85 #include <sys/wait.h>
86 #include <unistd.h>
87 #include <pwd.h>
88
89 #ifdef USE_SYSLOG
90 #include <syslog.h>
91 #ifndef LOG_AUTHPRIV
92 #define LOG_AUTHPRIV LOG_AUTH
93 #endif
94 #endif
95
96 #ifdef HAVE_GETSPNAM
97 #include <shadow.h>
98 #endif
99
100 #ifdef HAVE_PW_ENCRYPT
101 extern char *pw_encrypt();
102 #define crypt pw_encrypt
103 #endif
104
105 /*
106  * Read the password (one line) from fp.  We don't turn off echo
107  * because we expect input from a pipe.
108  */
109 static char *
110 get_line(fp)
111         FILE *fp;
112 {
113         static char buf[128];
114         char *cp;
115         int ch;
116
117         cp = buf;
118         while ((ch = getc(fp)) != EOF && ch != '\0' && ch != '\n') {
119                 if (cp >= buf + sizeof buf - 1)
120                         break;
121                 *cp++ = ch;
122         }
123         *cp = '\0';
124         return buf;
125 }
126
127 /*
128  * Get the password file entry for the current user.  If the name
129  * returned by getlogin() is correct (matches the current real uid),
130  * return the entry for that user.  Otherwise, return the entry (if
131  * any) matching the current real uid.  Return NULL on failure.
132  */
133 static struct passwd *
134 get_my_pwent()
135 {
136         uid_t uid = getuid();
137         char *name = getlogin();
138
139         if (name && *name) {
140                 struct passwd *pw = getpwnam(name);
141
142                 if (pw && pw->pw_uid == uid)
143                         return pw;
144         }
145         return getpwuid(uid);
146 }
147
148 /*
149  * Verify the password.  The system-dependent shadow support is here.
150  */
151 static int
152 password_auth_ok(pw, pass)
153         const struct passwd *pw;
154         const char *pass;
155 {
156         int result;
157         char *cp;
158 #ifdef HAVE_AUTH_METHODS
159         char *buf;
160 #endif
161 #ifdef HAVE_GETSPNAM
162         struct spwd *sp;
163 #endif
164
165         if (pw) {
166 #ifdef HAVE_GETSPNAM
167                 sp = getspnam(pw->pw_name);
168                 if (sp)
169                         cp = sp->sp_pwdp;
170                 else
171 #endif
172                         cp = pw->pw_passwd;
173         } else
174                 cp = "xx";
175
176 #ifdef HAVE_AUTH_METHODS
177         buf = strdup(cp);  /* will be modified by strtok() */
178         if (!buf) {
179                 fprintf(stderr, "Out of memory.\n");
180                 exit(13);
181         }
182         cp = strtok(buf, ";");
183         while (cp && *cp == '@')
184                 cp = strtok(NULL, ";");
185
186         /* fail if no password authentication for this user */
187         if (!cp)
188                 cp = "xx";
189 #endif
190
191         if (*pass || *cp)
192                 result = (strcmp(crypt(pass, cp), cp) == 0);
193         else
194                 result = 1;  /* user with no password */
195
196 #ifdef HAVE_AUTH_METHODS
197         free(buf);
198 #endif
199         return result;
200 }
201
202 /*
203  * Main program.
204  */
205 int
206 main(argc, argv)
207         int argc;
208         char **argv;
209 {
210         struct passwd *pw;
211         char *pass, *name;
212         char myname[32];
213
214 #ifdef USE_SYSLOG
215         openlog("pwdauth", LOG_PID | LOG_CONS, LOG_AUTHPRIV);
216 #endif
217         pw = get_my_pwent();
218         if (!pw) {
219 #ifdef USE_SYSLOG
220                 syslog(LOG_ERR, "can't get login name for uid %d.\n",
221                        (int) getuid());
222 #endif
223                 fprintf(stderr, "Who are you?\n");
224                 exit(2);
225         }
226         strncpy(myname, pw->pw_name, sizeof myname - 1);
227         myname[sizeof myname - 1] = '\0';
228         name = myname;
229
230         if (argc > 1) {
231                 name = argv[1];
232                 pw = getpwnam(name);
233         }
234
235         pass = get_line(stdin);
236         if (password_auth_ok(pw, pass)) {
237 #ifdef USE_SYSLOG
238                 syslog(pw->pw_uid ? LOG_INFO : LOG_NOTICE,
239                        "user `%s' entered correct password for `%.32s'.\n",
240                        myname, name);
241 #endif
242                 exit(0);
243         }
244 #ifdef USE_SYSLOG
245         /* be careful not to overrun the syslog buffer */
246         syslog((!pw || pw->pw_uid) ? LOG_NOTICE : LOG_WARNING,
247                "user `%s' entered incorrect password for `%.32s'.\n",
248                myname, name);
249 #endif
250 #ifdef FAIL_DELAY
251         sleep(FAIL_DELAY);
252 #endif
253         fprintf(stderr, "Wrong password.\n");
254         exit(1);
255 }
256
257 #if 0
258 /*
259  * You can use code similar to the following to run this program.
260  * Return values: >=0 - program exit status (use the <sys/wait.h>
261  * macros to get the exit code, it is shifted left by 8 bits),
262  * -1 - check errno.
263  */
264 int
265 verify_password(const char *username, const char *password)
266 {
267         int pipe_fd[2];
268         int pid, wpid, status;
269
270         if (pipe(pipe_fd))
271                 return -1;
272         
273         if ((pid = fork()) == 0) {
274                 char *arg[3];
275                 char *env[1];
276
277                 /* child */
278                 close(pipe_fd[1]);
279                 if (pipe_fd[0] != 0) {
280                         if (dup2(pipe_fd[0], 0) != 0)
281                                 _exit(127);
282                         close(pipe_fd[0]);
283                 }
284                 arg[0] = "/usr/bin/pwdauth";
285                 arg[1] = username;
286                 arg[2] = NULL;
287                 env[0] = NULL;
288                 execve(arg[0], arg, env);
289                 _exit(127);
290         } else if (pid == -1) {
291                 /* error */
292                 close(pipe_fd[0]);
293                 close(pipe_fd[1]);
294                 return -1;
295         }
296         /* parent */
297         close(pipe_fd[0]);
298         write(pipe_fd[1], password, strlen(password));
299         write(pipe_fd[1], "\n", 1);
300         close(pipe_fd[1]);
301
302         while ((wpid = wait(&status)) != pid) {
303                 if (wpid == -1)
304                         return -1;
305         }
306         return status;
307 }
308 #endif