1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <sys/stat.h>
38 #include <utmpx.h>
39 #include <pwd.h>
40 #include <dirent.h>
41 #include <sys/param.h>
42 #include <sys/acl.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <sys/console.h>
46 #include "ttymon.h"
47 #include "tmextern.h"
48 #include "tmstruct.h"
49
50 static char devbuf[BUFSIZ];
51 static char *devname;
52
53 static int parse_args();
54 static void ttymon_options();
55 static void getty_options();
56 static void usage();
57 static char *find_ttyname();
58
59 extern void tmchild();
60 extern int vml();
61
62 void revokedevaccess(char *, uid_t, gid_t, mode_t);
63 /* cannot include libdevinfo.h */
64 extern int di_devperm_logout(const char *);
65
66 /*
67 * ttymon_express - This is call when ttymon is invoked with args
68 * or invoked as getty
69 * - This special version of ttymon will monitor
70 * one port only
71 * - It is intended to be used when some process
72 * wants to have a login session on the fly
73 */
74 void
ttymon_express(int argc,char ** argv)75 ttymon_express(int argc, char **argv)
76 {
77 struct pmtab *pmtab;
78 struct sigaction sigact;
79 extern int Retry;
80 extern void open_device();
81 extern void read_ttydefs();
82 extern int checkut_line();
83 #ifdef DEBUG
84 extern FILE *Debugfp;
85 extern void opendebug();
86 #endif
87
88 #ifdef DEBUG
89 opendebug(TRUE);
90 #endif
91
92 sigact.sa_flags = 0;
93 sigact.sa_handler = SIG_IGN;
94 (void) sigemptyset(&sigact.sa_mask);
95 (void) sigaction(SIGINT, &sigact, NULL);
96
97 if ((pmtab = ALLOC_PMTAB) == PNULL) {
98 log("ttymon_express: ALLOC_PMTAB failed");
99 exit(1);
100 }
101
102 if (parse_args(argc, argv, pmtab) != 0) {
103 log("ttymon_express: parse_args failed");
104 exit(1);
105 }
106
107 read_ttydefs(NULL, FALSE);
108
109 if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0'))
110 while (checkut_line(pmtab->p_device))
111 sleep(15);
112
113 if ((pmtab->p_device == NULL) || (*(pmtab->p_device) == '\0')) {
114 devname = find_ttyname(0);
115 if ((devname == NULL) || (*devname == '\0')) {
116 log("ttyname cannot find the device on fd 0");
117 exit(1);
118 }
119 pmtab->p_device = devname;
120 #ifdef DEBUG
121 debug("ttymon_express: devname = %s", devname);
122 #endif
123 /*
124 * become session leader
125 * fd 0 is closed and reopened just to make sure
126 * controlling tty is set up right
127 */
128 (void) setsid();
129 (void) close(0);
130 revokedevaccess(pmtab->p_device, 0, 0, 0);
131 if (open(pmtab->p_device, O_RDWR) < 0) {
132 log("open %s failed: %s", pmtab->p_device,
133 strerror(errno));
134 exit(1);
135 }
136 if ((pmtab->p_modules != NULL) &&
137 (*(pmtab->p_modules) != '\0')) {
138 if (push_linedisc(0, pmtab->p_modules,
139 pmtab->p_device) == -1)
140 exit(1);
141 }
142 if (initial_termio(0, pmtab) == -1)
143 exit(1);
144 di_devperm_logout((const char *)pmtab->p_device);
145 } else {
146 (void) setsid();
147 (void) close(0);
148 Retry = FALSE;
149 open_device(pmtab);
150 if (Retry) /* open failed */
151 exit(1);
152 }
153 tmchild(pmtab);
154 exit(1); /*NOTREACHED*/
155 }
156
157 /*
158 * parse_arg - parse cmd line arguments
159 */
160 static int
parse_args(int argc,char ** argv,struct pmtab * pmtab)161 parse_args(int argc, char **argv, struct pmtab *pmtab)
162 {
163 static char p_server[] = "/usr/bin/login";
164 extern char *lastname();
165 extern void getty_account();
166 static char termbuf[MAX_TERM_TYPE_LEN];
167 static struct cons_getterm cnterm = {sizeof (termbuf), termbuf};
168
169 /* initialize fields to some default first */
170 pmtab->p_tag = "";
171 pmtab->p_flags = 0;
172 pmtab->p_identity = "root";
173 pmtab->p_res1 = "reserved";
174 pmtab->p_res2 = "reserved";
175 pmtab->p_res3 = "reserved";
176 pmtab->p_uid = 0;
177 pmtab->p_gid = 0;
178 pmtab->p_dir = "/";
179 pmtab->p_ttyflags = 0;
180 pmtab->p_count = 0;
181 pmtab->p_server = p_server;
182 pmtab->p_timeout = 0;
183 pmtab->p_modules = "";
184 pmtab->p_prompt = "login: ";
185 pmtab->p_dmsg = "";
186 pmtab->p_termtype = "";
187 pmtab->p_device = "";
188 pmtab->p_status = GETTY;
189 if (strcmp(lastname(argv[0]), "getty") == 0) {
190 pmtab->p_ttylabel = "300";
191 getty_options(argc, argv, pmtab);
192 } else {
193 int cn_fd;
194
195 pmtab->p_ttylabel = "9600";
196 ttymon_options(argc, argv, pmtab);
197
198 /*
199 * The following code is only reached if -g was specified.
200 * It attempts to determine a suitable terminal type for
201 * the console login process.
202 *
203 * If -d /dev/console also specified, we send an ioctl
204 * to the console device to query the TERM type.
205 *
206 * If any of the tests, system calls, or ioctls fail
207 * then pmtab->p_termtype retains its default value
208 * of "". otherwise it is set to a term type value
209 * that was returned.
210 */
211 if ((strlen(pmtab->p_termtype) == 0) &&
212 (strcmp(pmtab->p_device, "/dev/console") == 0) &&
213 ((cn_fd = open("/dev/console", O_RDONLY)) != -1)) {
214
215 if (ioctl(cn_fd, CONS_GETTERM, &cnterm) != -1)
216 pmtab->p_termtype = cnterm.cn_term_type;
217 (void) close(cn_fd);
218 }
219 }
220
221 if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0'))
222 getty_account(pmtab->p_device); /* utmp accounting */
223 return (0);
224 }
225
226
227 /*
228 * ttymon_options - scan and check args for ttymon express
229 */
230
231 static void
ttymon_options(int argc,char ** argv,struct pmtab * pmtab)232 ttymon_options(int argc, char **argv, struct pmtab *pmtab)
233 {
234 int c; /* option letter */
235 char *timeout;
236 int gflag = 0; /* -g seen */
237 int size = 0;
238 char tbuf[BUFSIZ];
239
240 extern char *optarg;
241 extern int optind;
242 extern void copystr();
243 extern char *strsave();
244 extern char *getword();
245
246 while ((c = getopt(argc, argv, "T:gd:ht:p:m:l:")) != -1) {
247 switch (c) {
248 case 'g':
249 gflag = 1;
250 break;
251 case 'd':
252 pmtab->p_device = optarg;
253 break;
254 case 'h':
255 pmtab->p_ttyflags &= ~H_FLAG;
256 break;
257
258 case 'T':
259 pmtab->p_termtype = optarg;
260 break;
261 /*
262 * case 'b':
263 * pmtab->p_ttyflags |= B_FLAG;
264 * pmtab->p_ttyflags |= R_FLAG;
265 * break;
266 */
267 case 't':
268 timeout = optarg;
269 while (*optarg) {
270 if (!isdigit(*optarg++)) {
271 log("Invalid argument for "
272 "\"-t\" -- number expected.");
273 usage();
274 }
275 }
276 pmtab->p_timeout = atoi(timeout);
277 break;
278 case 'p':
279 copystr(tbuf, optarg);
280 pmtab->p_prompt = strsave(getword(tbuf, &size, TRUE));
281 break;
282 case 'm':
283 pmtab->p_modules = optarg;
284 if (vml(pmtab->p_modules) != 0)
285 usage();
286 break;
287 case 'l':
288 pmtab->p_ttylabel = optarg;
289 break;
290 case '?':
291 usage();
292 break; /*NOTREACHED*/
293 }
294 }
295 if (optind < argc)
296 usage();
297
298 if (!gflag)
299 usage();
300 }
301
302 /*
303 * usage - print out a usage message
304 */
305
306 static void
usage()307 usage()
308 {
309 char *umsg = "Usage: ttymon\n ttymon -g [-h] [-d device] "
310 "[-l ttylabel] [-t timeout] [-p prompt] [-m modules]\n";
311
312 if (isatty(STDERR_FILENO))
313 (void) fprintf(stderr, "%s", umsg);
314 else
315 cons_printf(umsg);
316 exit(1);
317 }
318
319 /*
320 * getty_options - this is cut from getty.c
321 * - it scan getty cmd args
322 * - modification is made to stuff args in pmtab
323 */
324 static void
getty_options(argc,argv,pmtab)325 getty_options(argc, argv, pmtab)
326 int argc;
327 char **argv;
328 struct pmtab *pmtab;
329 {
330 char *ptr;
331
332 /*
333 * the pre-4.0 getty's hang_up_line() is a no-op.
334 * For compatibility, H_FLAG cannot be set for this "getty".
335 */
336 pmtab->p_ttyflags &= ~(H_FLAG);
337
338 while (--argc && **++argv == '-') {
339 for (ptr = *argv + 1; *ptr; ptr++)
340 switch (*ptr) {
341 case 'h':
342 break;
343 case 't':
344 if (isdigit(*++ptr)) {
345 (void) sscanf(ptr, "%d", &(pmtab->p_timeout));
346 while (isdigit(*++ptr));
347 ptr--;
348 } else if (--argc) {
349 if (isdigit(*(ptr = *++argv)))
350 (void) sscanf(ptr, "%d",
351 &(pmtab->p_timeout));
352 else {
353 log("getty: timeout argument <%s> "
354 "invalid", *argv);
355 exit(1);
356 }
357 }
358 break;
359
360 case 'c':
361 log("Use \"sttydefs -l\" to check /etc/ttydefs.");
362 exit(0);
363 default:
364 break;
365 }
366 }
367
368 if (argc < 1) {
369 log("getty: no terminal line specified.");
370 exit(1);
371 } else {
372 (void) strcat(devbuf, "/dev/");
373 (void) strcat(devbuf, *argv);
374 pmtab->p_device = devbuf;
375 }
376
377 if (--argc > 0) {
378 pmtab->p_ttylabel = *++argv;
379 }
380
381 /*
382 * every thing after this will be ignored
383 * i.e. termtype and linedisc are ignored
384 */
385 }
386
387 /*
388 * find_ttyname(fd) - find the name of device associated with fd.
389 * - it first tries utmpx to see if an entry exists
390 * - with my pid and ut_line is defined. If ut_line
391 * - is defined, it will see if the major and minor
392 * - number of fd and devname from utmpx match.
393 * - If utmpx search fails, ttyname(fd) will be called.
394 */
395 static char *
find_ttyname(fd)396 find_ttyname(fd)
397 int fd;
398 {
399 pid_t ownpid;
400 struct utmpx *u;
401 static struct stat statf, statu;
402 static char buf[BUFSIZ];
403
404 ownpid = getpid();
405 setutxent();
406 while ((u = getutxent()) != NULL) {
407 if (u->ut_pid == ownpid) {
408 if (strlen(u->ut_line) != 0) {
409 if (*(u->ut_line) != '/') {
410 (void) strcpy(buf, "/dev/");
411 (void) strncat(buf, u->ut_line,
412 sizeof (u->ut_line));
413 } else {
414 (void) strncat(buf, u->ut_line,
415 sizeof (u->ut_line));
416 }
417 }
418 else
419 u = NULL;
420 break;
421 }
422 }
423 endutxent();
424 if ((u != NULL) &&
425 (fstat(fd, &statf) == 0) &&
426 (stat(buf, &statu) == 0) &&
427 (statf.st_dev == statu.st_dev) &&
428 (statf.st_rdev == statu.st_rdev)) {
429 #ifdef DEBUG
430 debug("ttymon_express: find device name from utmpx.");
431 #endif
432 return (buf);
433 } else {
434 #ifdef DEBUG
435 debug("ttymon_express: calling ttyname to find device name.");
436 #endif
437 return (ttyname(fd));
438 }
439 }
440
441 /*
442 * Revoke all access to a device node and make sure that there are
443 * no interposed streams devices attached. Must be called before a
444 * device is actually opened.
445 * When fdetach is called, the underlying device node is revealed; it
446 * will have the previous owner and that owner can re-attach; so we
447 * retry until we win.
448 * Ignore non-existent devices.
449 */
450 void
revokedevaccess(char * dev,uid_t uid,gid_t gid,mode_t mode)451 revokedevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode)
452 {
453 do {
454 if (chown(dev, uid, gid) == -1)
455 return;
456 } while (fdetach(dev) == 0);
457
458 /* Remove ACLs */
459
460 (void) acl_strip(dev, uid, gid, mode);
461 }
462