xref: /openbsd/usr.bin/skeyaudit/skeyaudit.c (revision 3aaa63eb)
1*3aaa63ebSderaadt /*	$OpenBSD: skeyaudit.c,v 1.29 2019/06/28 13:35:03 deraadt Exp $	*/
2c2ff5ecdSmillert 
3c2ff5ecdSmillert /*
4bf198cc6Smillert  * Copyright (c) 1997, 2000, 2003 Todd C. Miller <millert@openbsd.org>
5c2ff5ecdSmillert  *
606f01696Smillert  * Permission to use, copy, modify, and distribute this software for any
706f01696Smillert  * purpose with or without fee is hereby granted, provided that the above
806f01696Smillert  * copyright notice and this permission notice appear in all copies.
9c2ff5ecdSmillert  *
1006f01696Smillert  * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
1106f01696Smillert  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
1206f01696Smillert  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
1306f01696Smillert  * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1406f01696Smillert  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
1506f01696Smillert  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1606f01696Smillert  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173adef2e4Smillert  *
183adef2e4Smillert  * Sponsored in part by the Defense Advanced Research Projects
193adef2e4Smillert  * Agency (DARPA) and Air Force Research Laboratory, Air Force
203adef2e4Smillert  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
21c2ff5ecdSmillert  */
22c2ff5ecdSmillert 
23fed231abSmillert #include <sys/wait.h>
24fed231abSmillert 
250165dd6fSmillert #include <err.h>
260165dd6fSmillert #include <errno.h>
273adef2e4Smillert #include <fcntl.h>
280165dd6fSmillert #include <limits.h>
29fed231abSmillert #include <login_cap.h>
300165dd6fSmillert #include <paths.h>
310165dd6fSmillert #include <pwd.h>
320165dd6fSmillert #include <stdio.h>
330165dd6fSmillert #include <stdlib.h>
340165dd6fSmillert #include <string.h>
350165dd6fSmillert #include <unistd.h>
360165dd6fSmillert #include <skey.h>
370165dd6fSmillert 
38c72b5b24Smillert void notify(struct passwd *, int, int);
39426a6f8cSkrw void sanitise_stdfd(void);
40c72b5b24Smillert FILE *runsendmail(struct passwd *, int *);
418959c9bfSmillert __dead void usage(void);
420165dd6fSmillert 
43426a6f8cSkrw void
sanitise_stdfd(void)44426a6f8cSkrw sanitise_stdfd(void)
45426a6f8cSkrw {
46426a6f8cSkrw 	int nullfd, dupfd;
47426a6f8cSkrw 
48426a6f8cSkrw 	if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
49426a6f8cSkrw 		fprintf(stderr, "Couldn't open /dev/null: %s\n",
50426a6f8cSkrw 		    strerror(errno));
51426a6f8cSkrw 		exit(1);
52426a6f8cSkrw 	}
53426a6f8cSkrw 	while (++dupfd <= STDERR_FILENO) {
54426a6f8cSkrw 		/* Only populate closed fds. */
55426a6f8cSkrw 		if (fcntl(dupfd, F_GETFL) == -1 && errno == EBADF) {
56426a6f8cSkrw 			if (dup2(nullfd, dupfd) == -1) {
57426a6f8cSkrw 				fprintf(stderr, "dup2: %s\n", strerror(errno));
58426a6f8cSkrw 				exit(1);
59426a6f8cSkrw 			}
60426a6f8cSkrw 		}
61426a6f8cSkrw 	}
62426a6f8cSkrw 	if (nullfd > STDERR_FILENO)
63426a6f8cSkrw 		close(nullfd);
64426a6f8cSkrw }
65426a6f8cSkrw 
660165dd6fSmillert int
main(int argc,char ** argv)678959c9bfSmillert main(int argc, char **argv)
680165dd6fSmillert {
690165dd6fSmillert 	struct passwd *pw;
700165dd6fSmillert 	struct skey key;
71d4eb3024Smillert 	char *name;
728959c9bfSmillert 	int ch, left, aflag, iflag, limit;
730165dd6fSmillert 
7449afeee2Stim 	if (pledge("stdio rpath wpath flock getpw proc exec id", NULL) == -1)
7549afeee2Stim 		err(1, "pledge");
7649afeee2Stim 
773adef2e4Smillert 	aflag = iflag = 0;
788959c9bfSmillert 	limit = 12;
79d4eb3024Smillert 	while ((ch = getopt(argc, argv, "ail:")) != -1)
800165dd6fSmillert 		switch(ch) {
81d4eb3024Smillert 		case 'a':
82d4eb3024Smillert 			if (getuid() != 0)
83d4eb3024Smillert 				errx(1, "only root may use the -a flag");
848959c9bfSmillert 			aflag = 1;
85d4eb3024Smillert 			break;
860165dd6fSmillert 		case 'i':
870165dd6fSmillert 			iflag = 1;
880165dd6fSmillert 			break;
890165dd6fSmillert 		case 'l':
900165dd6fSmillert 			errno = 0;
910165dd6fSmillert 			if ((limit = (int)strtol(optarg, NULL, 10)) == 0)
920165dd6fSmillert 				errno = ERANGE;
930165dd6fSmillert 			if (errno) {
940165dd6fSmillert 				warn("key limit");
950165dd6fSmillert 				usage();
960165dd6fSmillert 			}
970165dd6fSmillert 			break;
980165dd6fSmillert 		default:
990165dd6fSmillert 			usage();
1000165dd6fSmillert 	}
1010165dd6fSmillert 
10249afeee2Stim 	if (iflag) {
10349afeee2Stim 		if (pledge("stdio rpath wpath flock getpw", NULL) == -1)
10449afeee2Stim 			err(1, "pledge");
10549afeee2Stim 	}
10649afeee2Stim 
107426a6f8cSkrw 	 /* If we are in interactive mode, STDOUT_FILENO *must* be open. */
108426a6f8cSkrw 	if (iflag && fcntl(STDOUT_FILENO, F_GETFL) == -1 && errno == EBADF)
109426a6f8cSkrw 		exit(1);
110426a6f8cSkrw 
1113adef2e4Smillert 	/*
1123adef2e4Smillert 	 * Make sure STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO are open.
1133adef2e4Smillert 	 * If not, open /dev/null in their place or bail.
1143adef2e4Smillert 	 */
115426a6f8cSkrw 	sanitise_stdfd();
1163adef2e4Smillert 
1170165dd6fSmillert 	if (argc - optind > 0)
1180165dd6fSmillert 		usage();
1190165dd6fSmillert 
120d4eb3024Smillert 	/* Need key.keyfile zero'd at the very least */
121d4eb3024Smillert 	(void)memset(&key, 0, sizeof(key));
122d4eb3024Smillert 
1233adef2e4Smillert 	left = 0;
124d4eb3024Smillert 	if (aflag) {
125d4eb3024Smillert 		while ((ch = skeygetnext(&key)) == 0) {
126d4eb3024Smillert 			left = key.n - 1;
127d4eb3024Smillert 			if ((pw = getpwnam(key.logname)) == NULL)
128d4eb3024Smillert 				continue;
129d4eb3024Smillert 			if (left >= limit)
130d4eb3024Smillert 				continue;
1313adef2e4Smillert 			(void)fclose(key.keyfile);
1323adef2e4Smillert 			key.keyfile = NULL;
133fed231abSmillert 			notify(pw, left, iflag);
134d4eb3024Smillert 		}
135d4eb3024Smillert 		if (ch == -1)
136fc7f3601Smillert 			errx(-1, "cannot open %s", _PATH_SKEYDIR);
137d4eb3024Smillert 	} else {
1380165dd6fSmillert 		if ((pw = getpwuid(getuid())) == NULL)
1390165dd6fSmillert 			errx(1, "no passwd entry for uid %u", getuid());
1400165dd6fSmillert 		if ((name = strdup(pw->pw_name)) == NULL)
1410165dd6fSmillert 			err(1, "cannot allocate memory");
1420165dd6fSmillert 		sevenbit(name);
1430165dd6fSmillert 
1448959c9bfSmillert 		switch (skeylookup(&key, name)) {
1450165dd6fSmillert 			case 0:		/* Success! */
1460165dd6fSmillert 				left = key.n - 1;
1470165dd6fSmillert 				break;
1480165dd6fSmillert 			case -1:	/* File error */
1498959c9bfSmillert 				errx(1, "cannot open %s/%s", _PATH_SKEYDIR,
1508959c9bfSmillert 				    name);
1510165dd6fSmillert 				break;
1520165dd6fSmillert 			case 1:		/* Unknown user */
1538959c9bfSmillert 				errx(1, "user %s is not listed in %s", name,
154fc7f3601Smillert 				    _PATH_SKEYDIR);
1550165dd6fSmillert 		}
15642cc5412Smillert 		(void)fclose(key.keyfile);
1570165dd6fSmillert 
1588959c9bfSmillert 		if (left < limit)
159fed231abSmillert 			notify(pw, left, iflag);
1600165dd6fSmillert 	}
1610165dd6fSmillert 
1628959c9bfSmillert 	exit(0);
163d4eb3024Smillert }
164d4eb3024Smillert 
165d4eb3024Smillert void
notify(struct passwd * pw,int seq,int interactive)1668959c9bfSmillert notify(struct passwd *pw, int seq, int interactive)
167d4eb3024Smillert {
168b9fc9a72Sderaadt 	static char hostname[HOST_NAME_MAX+1];
1698959c9bfSmillert 	pid_t pid;
170d4eb3024Smillert 	FILE *out;
171d4eb3024Smillert 
172d4eb3024Smillert 	/* Only set this once */
173d4eb3024Smillert 	if (hostname[0] == '\0' && gethostname(hostname, sizeof(hostname)) == -1)
17421a5bbfaSmillert 		strlcpy(hostname, "unknown", sizeof(hostname));
175d4eb3024Smillert 
176d4eb3024Smillert 	if (interactive)
177d4eb3024Smillert 		out = stdout;
178d4eb3024Smillert 	else
179fed231abSmillert 		out = runsendmail(pw, &pid);
180d4eb3024Smillert 
181d4eb3024Smillert 	if (!interactive)
1820165dd6fSmillert 		(void)fprintf(out,
183775a2683Sderaadt 		   "Auto-Submitted: auto-generated\n"
184fed231abSmillert 		   "To: %s\nSubject: IMPORTANT action required\n", pw->pw_name);
1850165dd6fSmillert 
186188a098fSpjanzen 	if (seq)
1870165dd6fSmillert 		(void)fprintf(out,
1880165dd6fSmillert "\nYou are nearing the end of your current S/Key sequence for account\n\
1890165dd6fSmillert %s on system %s.\n\n\
190188a098fSpjanzen Your S/Key sequence number is now %d.  When it reaches zero\n\
191188a098fSpjanzen you will no longer be able to use S/Key to log into the system.\n\n",
192fed231abSmillert pw->pw_name, hostname, seq);
193188a098fSpjanzen 	else
194188a098fSpjanzen 		(void)fprintf(out,
195188a098fSpjanzen "\nYou are at the end of your current S/Key sequence for account\n\
196188a098fSpjanzen %s on system %s.\n\n\
197188a098fSpjanzen At this point you can no longer use S/Key to log into the system.\n\n",
198188a098fSpjanzen pw->pw_name, hostname);
199188a098fSpjanzen 	(void)fprintf(out,
200188a098fSpjanzen "Type \"skeyinit -s\" to reinitialize your sequence number.\n\n");
2010165dd6fSmillert 
2023adef2e4Smillert 	if (!interactive) {
2030165dd6fSmillert 		(void)fclose(out);
204d4eb3024Smillert 		(void)waitpid(pid, NULL, 0);
2050165dd6fSmillert 	}
2063adef2e4Smillert }
2070165dd6fSmillert 
208d4eb3024Smillert FILE *
runsendmail(struct passwd * pw,pid_t * pidp)2098959c9bfSmillert runsendmail(struct passwd *pw, pid_t *pidp)
210d4eb3024Smillert {
211d4eb3024Smillert 	FILE *fp;
2128959c9bfSmillert 	int pfd[2];
2138959c9bfSmillert 	pid_t pid;
214d4eb3024Smillert 
215*3aaa63ebSderaadt 	if (pipe(pfd) == -1)
216d4eb3024Smillert 		return(NULL);
217d4eb3024Smillert 
218d4eb3024Smillert 	switch (pid = fork()) {
219d4eb3024Smillert 	case -1:			/* fork(2) failed */
220d4eb3024Smillert 		(void)close(pfd[0]);
221d4eb3024Smillert 		(void)close(pfd[1]);
222d4eb3024Smillert 		return(NULL);
223d4eb3024Smillert 	case 0:				/* In child */
224d4eb3024Smillert 		(void)close(pfd[1]);
225d4eb3024Smillert 		(void)dup2(pfd[0], STDIN_FILENO);
226d4eb3024Smillert 		(void)close(pfd[0]);
227d4eb3024Smillert 
228d4eb3024Smillert 		/* Run sendmail as target user not root */
2293b08765cSmillert 		if (getuid() == 0 &&
2303b08765cSmillert 		    setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0) {
231fed231abSmillert 			warn("cannot set user context");
232fed231abSmillert 			_exit(127);
233fed231abSmillert 		}
234d4eb3024Smillert 
235c96f6a27Sderaadt 		execl(_PATH_SENDMAIL, "sendmail", "-t", (char *)NULL);
236d4eb3024Smillert 		warn("cannot run \"%s -t\"", _PATH_SENDMAIL);
237d4eb3024Smillert 		_exit(127);
238d4eb3024Smillert 	}
239d4eb3024Smillert 
240d4eb3024Smillert 	/* In parent */
241d4eb3024Smillert 	*pidp = pid;
242d4eb3024Smillert 	fp = fdopen(pfd[1], "w");
243d4eb3024Smillert 	(void)close(pfd[0]);
244d4eb3024Smillert 
245d4eb3024Smillert 	return(fp);
246d4eb3024Smillert }
2478959c9bfSmillert 
2488959c9bfSmillert __dead void
usage(void)2498959c9bfSmillert usage(void)
2500165dd6fSmillert {
2518959c9bfSmillert 	extern char *__progname;
2528959c9bfSmillert 
253a9050c90Ssobrado 	(void)fprintf(stderr, "usage: %s [-ai] [-l limit]\n",
2540165dd6fSmillert 	    __progname);
2550165dd6fSmillert 	exit(1);
2560165dd6fSmillert }
257