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
sanitise_stdfd(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
main(int argc,char ** argv)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
notify(struct passwd * pw,int seq,int interactive)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 *
runsendmail(struct passwd * pw,pid_t * pidp)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
usage(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