1 /*****************************************************************
3 * kcheckpass - Simple password checker
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the Free
17 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * kcheckpass is a simple password checker. Just invoke and
21 * send it the password on stdin.
23 * If the password was accepted, the program exits with 0;
24 * if it was rejected, it exits with 1. Any other exit
25 * code signals an error.
27 * It's hopefully simple enough to allow it to be setuid
30 * Compile with -DHAVE_VSYSLOG if you have vsyslog().
31 * Compile with -DHAVE_PAM if you have a PAM system,
32 * and link with -lpam -ldl.
33 * Compile with -DHAVE_SHADOW if you have a shadow
36 * Copyright (C) 1998, Caldera, Inc.
37 * Released under the GNU General Public License
39 * Olaf Kirch <okir@caldera.de> General Framework and PAM support
40 * Christian Esken <esken@kde.org> Shadow and /etc/passwd support
41 * Roberto Teixeira <maragato@kde.org> other user (-U) support
42 * Oswald Buddenhagen <ossi@kde.org> Binary server mode
44 * Other parts were taken from kscreensaver's passwd.cpp.
46 *****************************************************************/
48 #include "kcheckpass.h"
60 /* Compatibility: accept some options from environment variables */
65 static int havetty
, sfd
= -1, nullpass
;
68 conv_legacy (ConvRequest what
, const char *prompt
)
78 /* there is no prompt == 0 case */
81 /* i guess we should use /dev/tty ... */
82 fputs(prompt
, stdout
);
84 if (!fgets(buf
, sizeof(buf
), stdin
))
87 if (len
&& buf
[len
- 1] == '\n')
92 #ifdef HAVE_GETPASSPHRASE
93 p
= getpassphrase(prompt
? prompt
: "Password: ");
95 p
= getpass(prompt
? prompt
: "Password: ");
98 memset(p
, 0, strlen(p
));
103 if ((len
= read(0, buf
, sizeof(buf
) - 1)) < 0) {
104 message("Cannot read password\n");
107 if (len
&& buf
[len
- 1] == '\n')
116 message("Information: %s\n", prompt
);
119 message("Error: %s\n", prompt
);
122 message("Authentication backend requested data type which cannot be handled.\n");
128 Reader (void *buf
, int count
)
132 for (rlen
= 0; rlen
< count
; ) {
134 ret
= read (sfd
, (void *)((char *)buf
+ rlen
), count
- rlen
);
150 GRead (void *buf
, int count
)
152 if (Reader (buf
, count
) != count
) {
153 message ("Communication breakdown on read\n");
159 GWrite (const void *buf
, int count
)
161 if (write (sfd
, buf
, count
) != count
) {
162 message ("Communication breakdown on write\n");
170 GWrite (&val
, sizeof(val
));
174 GSendStr (const char *buf
)
176 unsigned len
= buf
? strlen (buf
) + 1 : 0;
177 GWrite (&len
, sizeof(len
));
182 GSendArr (int len
, const char *buf
)
184 GWrite (&len
, sizeof(len
));
193 GRead (&val
, sizeof(val
));
203 if (!(len
= GRecvInt()))
205 if (len
> 0x1000 || !(buf
= malloc (len
))) {
206 message ("No memory for read buffer\n");
210 buf
[len
- 1] = 0; /* we're setuid ... don't trust "them" */
220 if (!(len
= (unsigned) GRecvInt()))
222 if (len
> 0x10000 || !(arr
= malloc (len
))) {
223 message ("No memory for read buffer\n");
232 conv_server (ConvRequest what
, const char *prompt
)
238 unsigned const char *up
= (unsigned const char *)prompt
;
239 int len
= up
[3] | (up
[2] << 8) | (up
[1] << 16) | (up
[0] << 24);
240 GSendArr (len
, prompt
);
249 if (msg
&& (GRecvInt() & IsPassword
) && !*msg
)
262 message(const char *fmt
, ...)
267 vfprintf(stderr
, fmt
, ap
);
272 # define O_NOFOLLOW 0
275 static void ATTR_NORETURN
279 "usage: kcheckpass {-h|[-c caller] [-m method] [-U username|-S handle]}\n"
281 " -h this help message\n"
282 " -U username authenticate the specified user instead of current user\n"
283 " -S handle operate in binary server mode on file descriptor handle\n"
284 " -c caller the calling application, effectively the PAM service basename\n"
285 " -m method use the specified authentication method (default: \"classic\")\n"
288 " 1 invalid password\n"
289 " 2 cannot read password database\n"
290 " Anything else tells you something's badly hosed.\n"
296 main(int argc
, char **argv
)
299 const char *caller
= KCHECKPASS_PAM_SERVICE
;
301 const char *method
= "classic";
302 const char *username
= 0;
312 char fname
[64], fcont
[64];
314 #ifdef HAVE_OSF_C2_PASSWD
315 initialize_osf_security(argc
, argv
);
318 /* Make sure stdout/stderr are open */
319 for (c
= 1; c
<= 2; c
++) {
320 if (fcntl(c
, F_GETFL
) == -1) {
321 if ((nfd
= open("/dev/null", O_WRONLY
)) < 0) {
322 message("cannot open /dev/null: %s\n", strerror(errno
));
334 while ((c
= getopt(argc
, argv
, "hc:m:U:S:")) != -1) {
354 message("Command line option parsing error\n");
361 if ((p
= getenv("KDE_PAM_ACTION")))
364 if ((p
= getenv("KCHECKPASS_USER")))
370 if (!(p
= getenv("LOGNAME")) || !(pw
= getpwnam(p
)) || pw
->pw_uid
!= uid
)
371 if (!(p
= getenv("USER")) || !(pw
= getpwnam(p
)) || pw
->pw_uid
!= uid
)
372 if (!(pw
= getpwuid(uid
))) {
373 message("Cannot determinate current user\n");
376 if (!(username
= strdup(pw
->pw_name
))) {
377 message("Out of memory\n");
383 * Throttle kcheckpass invocations to avoid abusing it for bruteforcing
384 * the password. This delay belongs to the *previous* invocation, where
385 * we can't enforce it reliably (without risking giving away the result
386 * before it is due). We don't differentiate between success and failure -
387 * it's not expected to have a noticeable adverse effect.
389 if ( uid
!= geteuid() ) {
390 sprintf(fname
, "/var/run/kcheckpass.%d", uid
);
391 if ((lfd
= open(fname
, O_RDWR
| O_CREAT
| O_NOFOLLOW
, 0600)) < 0) {
392 message("Cannot open lockfile\n");
397 lk
.l_whence
= SEEK_SET
;
398 lk
.l_start
= lk
.l_len
= 0;
399 if (fcntl(lfd
, F_SETLKW
, &lk
)) {
400 message("Cannot obtain lock\n");
404 if ((c
= read(lfd
, fcont
, sizeof(fcont
)-1)) > 0 &&
405 (fcont
[c
] = '\0', sscanf(fcont
, "%ld", &nexttime
) == 1))
408 if (nexttime
> ct
&& nexttime
< ct
+ THROTTLE
)
409 sleep(nexttime
- ct
);
412 lseek(lfd
, 0, SEEK_SET
);
413 write(lfd
, fcont
, sprintf(fcont
, "%lu\n", time(0) + THROTTLE
));
418 /* Now do the fandango */
425 sfd
< 0 ? conv_legacy
: conv_server
);
427 if (ret
== AuthBad
) {
428 message("Authentication failure\n");
430 openlog("kcheckpass", LOG_PID
, LOG_AUTH
);
431 syslog(LOG_NOTICE
, "Authentication failure for %s (invoked by uid %d)", username
, uid
);
441 memset(str
, 0, strlen(str
));
445 /*****************************************************************
446 The real authentication methods are in separate source files.
447 Look in checkpass_*.c
448 *****************************************************************/