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