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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  *
22  * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
28  * All Rights Reserved.
29  */
30 
31 /*
32  * University Copyright- Copyright (c) 1982, 1986, 1988
33  * The Regents of the University of California.
34  * All Rights Reserved.
35  *
36  * University Acknowledgment- Portions of this document are derived from
37  * software developed by the University of California, Berkeley, and its
38  * contributors.
39  */
40 
41 #pragma ident	"%Z%%M%	%I%	%E% SMI"
42 
43 
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <sys/ioctl.h>
47 #include <sys/fcntl.h>
48 #include <sys/time.h>
49 #include <time.h>
50 #include <stdio.h>
51 #include <sys/wait.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <pwd.h>
55 #include <unistd.h>
56 #include <ctype.h>
57 #include <string.h>
58 #include "talkd_impl.h"
59 
60 static int nofork = 0;		/* to be set from the debugger */
61 
62 static int announce_proc(CTL_MSG *request, char *remote_machine);
63 static void print_mesg(FILE *tf, CTL_MSG *request, char *remote_machine);
64 
65 /*
66  * Because the tty driver insists on attaching a terminal-less
67  * process to any terminal that it writes on, we must fork a child
68  * to protect ourselves.
69  */
70 
71 int
72 announce(CTL_MSG *request, char *remote_machine)
73 {
74 	pid_t pid, val;
75 	int status;
76 
77 	if (nofork) {
78 		return (announce_proc(request, remote_machine));
79 	}
80 
81 	if (pid = fork()) {
82 
83 		/* we are the parent, so wait for the child */
84 		if (pid == (pid_t)-1) {
85 			/* the fork failed */
86 			return (FAILED);
87 		}
88 
89 		do {
90 			val = wait(&status);
91 			if (val == (pid_t)-1) {
92 				if (errno == EINTR) {
93 					continue;
94 				} else {
95 					/* shouldn't happen */
96 					print_error("wait");
97 					return (FAILED);
98 				}
99 			}
100 		} while (val != pid);
101 
102 		if ((status & 0377) > 0) {
103 			/* we were killed by some signal */
104 			return (FAILED);
105 		}
106 
107 		/* Get the second byte, this is the exit/return code */
108 		return ((status>>8)&0377);
109 	} else {
110 		/* we are the child, go and do it */
111 		_exit(announce_proc(request, remote_machine));
112 	}
113 	/* NOTREACHED */
114 }
115 
116 
117 /*
118  * See if the user is accepting messages. If so, announce that
119  * a talk is requested.
120  */
121 static int
122 announce_proc(CTL_MSG *request, char *remote_machine)
123 {
124 #define	TTY_BUFSZ	32
125 	char full_tty[TTY_BUFSZ];
126 	FILE *tf;
127 	struct stat stbuf;
128 	int fd;
129 	struct passwd *p;
130 
131 	(void) snprintf(full_tty, TTY_BUFSZ, "/dev/%s", request->r_tty);
132 	p = getpwnam(request->r_name);
133 
134 	if (p == 0 || access(full_tty, 0) != 0) {
135 		return (FAILED);
136 	}
137 
138 	/* fopen uses O_CREAT|O_TRUNC, we don't want that */
139 	if ((fd = open(full_tty, O_WRONLY|O_NONBLOCK)) == -1) {
140 		return (PERMISSION_DENIED);
141 	}
142 	/* must be tty */
143 	if (!isatty(fd)) {
144 		(void) close(fd);
145 		return (PERMISSION_DENIED);
146 	}
147 
148 	/*
149 	 * open gratuitously attaches the talkd to any tty it opens, so
150 	 * disconnect us from the tty before we catch a signal
151 	 */
152 	(void) setsid();
153 
154 	if (fstat(fd, &stbuf) < 0 || stbuf.st_uid != p->pw_uid) {
155 		(void) close(fd);
156 		return (PERMISSION_DENIED);
157 	}
158 
159 	if ((stbuf.st_mode&020) == 0) {
160 		(void) close(fd);
161 		return (PERMISSION_DENIED);
162 	}
163 
164 	if ((tf = fdopen(fd, "w")) == NULL) {
165 		(void) close(fd);
166 		return (PERMISSION_DENIED);
167 	}
168 
169 	print_mesg(tf, request, remote_machine);
170 	(void) fclose(tf);
171 	return (SUCCESS);
172 }
173 
174 #define	max(a, b) ((a) > (b) ? (a) : (b))
175 #define	N_LINES	5
176 #define	N_CHARS	300
177 
178 /*
179  * Build a block of characters containing the message.
180  * It is sent blank filled and in a single block to
181  * try to keep the message in one piece if the recipient
182  * is in vi at the time.
183  */
184 static void
185 print_mesg(FILE *tf, CTL_MSG *request, char *remote_machine)
186 {
187 	struct timeval clock;
188 	struct tm *localclock;
189 	char line_buf[N_LINES][N_CHARS];
190 	int sizes[N_LINES];
191 	char *bptr, *lptr;
192 	int i, j, max_size;
193 
194 	/*
195 	 * [3 wakeup chars + (lines * max chars/line) +
196 	 * (lines * strlen("\r\n")) + 1(NUL)].
197 	 */
198 	char big_buf[3 + (N_LINES * (N_CHARS - 1)) + (N_LINES * 2) + 1];
199 	/*
200 	 * ( (length of (request->l_name) - 1(NUL)) *
201 	 * (strlen("M-") + 1('^') + 1(printable char)) ) + 1(NUL).
202 	 */
203 	char l_username[((NAME_SIZE - 1) * 4) + 1];
204 	int len, k;
205 
206 	i = 0;
207 	max_size = 0;
208 
209 	(void) gettimeofday(&clock, NULL);
210 	localclock = localtime(&clock.tv_sec);
211 
212 	(void) sprintf(line_buf[i], " ");
213 
214 	sizes[i] = strlen(line_buf[i]);
215 	max_size = max(max_size, sizes[i]);
216 	i++;
217 
218 	(void) snprintf(line_buf[i], N_CHARS,
219 	    "Message from Talk_Daemon@%s at %d:%02d ...", hostname,
220 	    localclock->tm_hour, localclock->tm_min);
221 
222 	sizes[i] = strlen(line_buf[i]);
223 	max_size = max(max_size, sizes[i]);
224 	i++;
225 
226 	len = (strlen(request->l_name) > NAME_SIZE - 1) ? (NAME_SIZE - 1) :
227 	    strlen(request->l_name);
228 	for (j = 0, k = 0; j < len; j++) {
229 		if (!isprint((unsigned char)request->l_name[j])) {
230 			char c;
231 			if (!isascii((unsigned char)request->l_name[j])) {
232 				l_username[k++] = 'M';
233 				l_username[k++] = '-';
234 				c = toascii(request->l_name[j]);
235 			}
236 			if (iscntrl((unsigned char)request->l_name[j])) {
237 				l_username[k++] = '^';
238 				/* add decimal 64 to the control character */
239 				c = request->l_name[j] + 0100;
240 			}
241 			l_username[k++] = c;
242 		} else {
243 			l_username[k++] = request->l_name[j];
244 		}
245 	}
246 	l_username[k] = '\0';
247 
248 	(void) snprintf(line_buf[i], N_CHARS,
249 	    "talk: connection requested by %s@%s.", l_username, remote_machine);
250 
251 	sizes[i] = strlen(line_buf[i]);
252 	max_size = max(max_size, sizes[i]);
253 	i++;
254 
255 	(void) snprintf(line_buf[i], N_CHARS, "talk: respond with:  talk %s@%s",
256 	    l_username, remote_machine);
257 
258 	sizes[i] = strlen(line_buf[i]);
259 	max_size = max(max_size, sizes[i]);
260 	i++;
261 
262 	(void) sprintf(line_buf[i], " ");
263 
264 	sizes[i] = strlen(line_buf[i]);
265 	max_size = max(max_size, sizes[i]);
266 	i++;
267 
268 	bptr = big_buf;
269 	*(bptr++) = '\a';	/* send something to wake them up */
270 	*(bptr++) = '\r';	/* add a \r in case of raw mode */
271 	*(bptr++) = '\n';
272 	for (i = 0; i < N_LINES; i++) {
273 		/* copy the line into the big buffer */
274 		lptr = line_buf[i];
275 		while (*lptr != '\0') {
276 			*(bptr++) = *(lptr++);
277 		}
278 
279 		/* pad out the rest of the lines with blanks */
280 		for (j = sizes[i]; j < max_size; j++) {
281 			*(bptr++) = ' ';
282 		}
283 
284 		*(bptr++) = '\r';	/* add a \r in case of raw mode */
285 		*(bptr++) = '\n';
286 	}
287 	*bptr = '\0';
288 
289 	(void) fputs(big_buf, tf);
290 	(void) fflush(tf);
291 	(void) setsid();
292 }
293