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" */
219 unsigned const char *up
;
221 if (!(len
= (unsigned) GRecvInt()))
224 message ("Too short binary authentication data block\n");
227 if (len
> 0x10000 || !(arr
= malloc (len
))) {
228 message ("No memory for read buffer\n");
232 up
= (unsigned const char *)arr
;
233 if (len
!= (unsigned)(up
[3] | (up
[2] << 8) | (up
[1] << 16) | (up
[0] << 24))) {
234 message ("Mismatched binary authentication data block size\n");
242 conv_server (ConvRequest what
, const char *prompt
)
248 unsigned const char *up
= (unsigned const char *)prompt
;
249 int len
= up
[3] | (up
[2] << 8) | (up
[1] << 16) | (up
[0] << 24);
250 GSendArr (len
, prompt
);
259 if (msg
&& (GRecvInt() & IsPassword
) && !*msg
)
272 message(const char *fmt
, ...)
277 vfprintf(stderr
, fmt
, ap
);
282 # define O_NOFOLLOW 0
285 static void ATTR_NORETURN
289 "usage: kcheckpass {-h|[-c caller] [-m method] [-U username|-S handle]}\n"
291 " -h this help message\n"
292 " -U username authenticate the specified user instead of current user\n"
293 " -S handle operate in binary server mode on file descriptor handle\n"
294 " -c caller the calling application, effectively the PAM service basename\n"
295 " -m method use the specified authentication method (default: \"classic\")\n"
298 " 1 invalid password\n"
299 " 2 cannot read password database\n"
300 " Anything else tells you something's badly hosed.\n"
306 main(int argc
, char **argv
)
309 const char *caller
= KCHECKPASS_PAM_SERVICE
;
311 const char *method
= "classic";
312 const char *username
= 0;
322 char fname
[64], fcont
[64];
324 #ifdef HAVE_OSF_C2_PASSWD
325 initialize_osf_security(argc
, argv
);
328 /* Make sure stdout/stderr are open */
329 for (c
= 1; c
<= 2; c
++) {
330 if (fcntl(c
, F_GETFL
) == -1) {
331 if ((nfd
= open("/dev/null", O_WRONLY
)) < 0) {
332 message("cannot open /dev/null: %s\n", strerror(errno
));
344 while ((c
= getopt(argc
, argv
, "hc:m:U:S:")) != -1) {
364 message("Command line option parsing error\n");
371 if ((p
= getenv("KDE_PAM_ACTION")))
374 if ((p
= getenv("KCHECKPASS_USER")))
380 if (!(p
= getenv("LOGNAME")) || !(pw
= getpwnam(p
)) || pw
->pw_uid
!= uid
)
381 if (!(p
= getenv("USER")) || !(pw
= getpwnam(p
)) || pw
->pw_uid
!= uid
)
382 if (!(pw
= getpwuid(uid
))) {
383 message("Cannot determinate current user\n");
386 if (!(username
= strdup(pw
->pw_name
))) {
387 message("Out of memory\n");
393 * Throttle kcheckpass invocations to avoid abusing it for bruteforcing
394 * the password. This delay belongs to the *previous* invocation, where
395 * we can't enforce it reliably (without risking giving away the result
396 * before it is due). We don't differentiate between success and failure -
397 * it's not expected to have a noticeable adverse effect.
399 if ( uid
!= geteuid() ) {
400 sprintf(fname
, "/var/run/kcheckpass.%d", uid
);
401 if ((lfd
= open(fname
, O_RDWR
| O_CREAT
| O_NOFOLLOW
, 0600)) < 0) {
402 message("Cannot open lockfile\n");
407 lk
.l_whence
= SEEK_SET
;
408 lk
.l_start
= lk
.l_len
= 0;
409 if (fcntl(lfd
, F_SETLKW
, &lk
)) {
410 message("Cannot obtain lock\n");
414 if ((c
= read(lfd
, fcont
, sizeof(fcont
)-1)) > 0 &&
415 (fcont
[c
] = '\0', sscanf(fcont
, "%ld", &nexttime
) == 1))
418 if (nexttime
> ct
&& nexttime
< ct
+ THROTTLE
)
419 sleep(nexttime
- ct
);
422 lseek(lfd
, 0, SEEK_SET
);
423 write(lfd
, fcont
, sprintf(fcont
, "%lu\n", time(0) + THROTTLE
));
428 /* Now do the fandango */
435 sfd
< 0 ? conv_legacy
: conv_server
);
437 if (ret
== AuthBad
) {
438 message("Authentication failure\n");
440 openlog("kcheckpass", LOG_PID
, LOG_AUTH
);
441 syslog(LOG_NOTICE
, "Authentication failure for %s (invoked by uid %d)", username
, uid
);
451 memset(str
, 0, strlen(str
));
455 /*****************************************************************
456 The real authentication methods are in separate source files.
457 Look in checkpass_*.c
458 *****************************************************************/