xref: /openbsd/usr.bin/skeyaudit/skeyaudit.c (revision 3cab2bb3)
1 /*	$OpenBSD: skeyaudit.c,v 1.29 2019/06/28 13:35:03 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1997, 2000, 2003 Todd C. Miller <millert@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
12  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
13  * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * Sponsored in part by the Defense Advanced Research Projects
19  * Agency (DARPA) and Air Force Research Laboratory, Air Force
20  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
21  */
22 
23 #include <sys/wait.h>
24 
25 #include <err.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <limits.h>
29 #include <login_cap.h>
30 #include <paths.h>
31 #include <pwd.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <skey.h>
37 
38 void notify(struct passwd *, int, int);
39 void sanitise_stdfd(void);
40 FILE *runsendmail(struct passwd *, int *);
41 __dead void usage(void);
42 
43 void
44 sanitise_stdfd(void)
45 {
46 	int nullfd, dupfd;
47 
48 	if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
49 		fprintf(stderr, "Couldn't open /dev/null: %s\n",
50 		    strerror(errno));
51 		exit(1);
52 	}
53 	while (++dupfd <= STDERR_FILENO) {
54 		/* Only populate closed fds. */
55 		if (fcntl(dupfd, F_GETFL) == -1 && errno == EBADF) {
56 			if (dup2(nullfd, dupfd) == -1) {
57 				fprintf(stderr, "dup2: %s\n", strerror(errno));
58 				exit(1);
59 			}
60 		}
61 	}
62 	if (nullfd > STDERR_FILENO)
63 		close(nullfd);
64 }
65 
66 int
67 main(int argc, char **argv)
68 {
69 	struct passwd *pw;
70 	struct skey key;
71 	char *name;
72 	int ch, left, aflag, iflag, limit;
73 
74 	if (pledge("stdio rpath wpath flock getpw proc exec id", NULL) == -1)
75 		err(1, "pledge");
76 
77 	aflag = iflag = 0;
78 	limit = 12;
79 	while ((ch = getopt(argc, argv, "ail:")) != -1)
80 		switch(ch) {
81 		case 'a':
82 			if (getuid() != 0)
83 				errx(1, "only root may use the -a flag");
84 			aflag = 1;
85 			break;
86 		case 'i':
87 			iflag = 1;
88 			break;
89 		case 'l':
90 			errno = 0;
91 			if ((limit = (int)strtol(optarg, NULL, 10)) == 0)
92 				errno = ERANGE;
93 			if (errno) {
94 				warn("key limit");
95 				usage();
96 			}
97 			break;
98 		default:
99 			usage();
100 	}
101 
102 	if (iflag) {
103 		if (pledge("stdio rpath wpath flock getpw", NULL) == -1)
104 			err(1, "pledge");
105 	}
106 
107 	 /* If we are in interactive mode, STDOUT_FILENO *must* be open. */
108 	if (iflag && fcntl(STDOUT_FILENO, F_GETFL) == -1 && errno == EBADF)
109 		exit(1);
110 
111 	/*
112 	 * Make sure STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO are open.
113 	 * If not, open /dev/null in their place or bail.
114 	 */
115 	sanitise_stdfd();
116 
117 	if (argc - optind > 0)
118 		usage();
119 
120 	/* Need key.keyfile zero'd at the very least */
121 	(void)memset(&key, 0, sizeof(key));
122 
123 	left = 0;
124 	if (aflag) {
125 		while ((ch = skeygetnext(&key)) == 0) {
126 			left = key.n - 1;
127 			if ((pw = getpwnam(key.logname)) == NULL)
128 				continue;
129 			if (left >= limit)
130 				continue;
131 			(void)fclose(key.keyfile);
132 			key.keyfile = NULL;
133 			notify(pw, left, iflag);
134 		}
135 		if (ch == -1)
136 			errx(-1, "cannot open %s", _PATH_SKEYDIR);
137 	} else {
138 		if ((pw = getpwuid(getuid())) == NULL)
139 			errx(1, "no passwd entry for uid %u", getuid());
140 		if ((name = strdup(pw->pw_name)) == NULL)
141 			err(1, "cannot allocate memory");
142 		sevenbit(name);
143 
144 		switch (skeylookup(&key, name)) {
145 			case 0:		/* Success! */
146 				left = key.n - 1;
147 				break;
148 			case -1:	/* File error */
149 				errx(1, "cannot open %s/%s", _PATH_SKEYDIR,
150 				    name);
151 				break;
152 			case 1:		/* Unknown user */
153 				errx(1, "user %s is not listed in %s", name,
154 				    _PATH_SKEYDIR);
155 		}
156 		(void)fclose(key.keyfile);
157 
158 		if (left < limit)
159 			notify(pw, left, iflag);
160 	}
161 
162 	exit(0);
163 }
164 
165 void
166 notify(struct passwd *pw, int seq, int interactive)
167 {
168 	static char hostname[HOST_NAME_MAX+1];
169 	pid_t pid;
170 	FILE *out;
171 
172 	/* Only set this once */
173 	if (hostname[0] == '\0' && gethostname(hostname, sizeof(hostname)) == -1)
174 		strlcpy(hostname, "unknown", sizeof(hostname));
175 
176 	if (interactive)
177 		out = stdout;
178 	else
179 		out = runsendmail(pw, &pid);
180 
181 	if (!interactive)
182 		(void)fprintf(out,
183 		   "Auto-Submitted: auto-generated\n"
184 		   "To: %s\nSubject: IMPORTANT action required\n", pw->pw_name);
185 
186 	if (seq)
187 		(void)fprintf(out,
188 "\nYou are nearing the end of your current S/Key sequence for account\n\
189 %s on system %s.\n\n\
190 Your S/Key sequence number is now %d.  When it reaches zero\n\
191 you will no longer be able to use S/Key to log into the system.\n\n",
192 pw->pw_name, hostname, seq);
193 	else
194 		(void)fprintf(out,
195 "\nYou are at the end of your current S/Key sequence for account\n\
196 %s on system %s.\n\n\
197 At this point you can no longer use S/Key to log into the system.\n\n",
198 pw->pw_name, hostname);
199 	(void)fprintf(out,
200 "Type \"skeyinit -s\" to reinitialize your sequence number.\n\n");
201 
202 	if (!interactive) {
203 		(void)fclose(out);
204 		(void)waitpid(pid, NULL, 0);
205 	}
206 }
207 
208 FILE *
209 runsendmail(struct passwd *pw, pid_t *pidp)
210 {
211 	FILE *fp;
212 	int pfd[2];
213 	pid_t pid;
214 
215 	if (pipe(pfd) == -1)
216 		return(NULL);
217 
218 	switch (pid = fork()) {
219 	case -1:			/* fork(2) failed */
220 		(void)close(pfd[0]);
221 		(void)close(pfd[1]);
222 		return(NULL);
223 	case 0:				/* In child */
224 		(void)close(pfd[1]);
225 		(void)dup2(pfd[0], STDIN_FILENO);
226 		(void)close(pfd[0]);
227 
228 		/* Run sendmail as target user not root */
229 		if (getuid() == 0 &&
230 		    setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0) {
231 			warn("cannot set user context");
232 			_exit(127);
233 		}
234 
235 		execl(_PATH_SENDMAIL, "sendmail", "-t", (char *)NULL);
236 		warn("cannot run \"%s -t\"", _PATH_SENDMAIL);
237 		_exit(127);
238 	}
239 
240 	/* In parent */
241 	*pidp = pid;
242 	fp = fdopen(pfd[1], "w");
243 	(void)close(pfd[0]);
244 
245 	return(fp);
246 }
247 
248 __dead void
249 usage(void)
250 {
251 	extern char *__progname;
252 
253 	(void)fprintf(stderr, "usage: %s [-ai] [-l limit]\n",
254 	    __progname);
255 	exit(1);
256 }
257