xref: /openbsd-src/libexec/talkd/process.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: process.c,v 1.20 2013/11/27 21:25:24 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1983 Regents of the University of California.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 /*
33  * process.c handles the requests, which can be of three types:
34  *	ANNOUNCE - announce to a user that a talk is wanted
35  *	LEAVE_INVITE - insert the request into the table
36  *	LOOK_UP - look up to see if a request is waiting in
37  *		  in the table for the local user
38  *	DELETE - delete invitation
39  */
40 #include <sys/param.h>
41 #include <sys/stat.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <protocols/talkd.h>
46 #include <netdb.h>
47 #include <syslog.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <ctype.h>
51 #include <paths.h>
52 #include "talkd.h"
53 
54 #define	satosin(sa)	((struct sockaddr_in *)(sa))
55 
56 void
57 process_request(CTL_MSG *mp, CTL_RESPONSE *rp)
58 {
59 	CTL_MSG *ptr;
60 	char *s;
61 
62 	rp->vers = TALK_VERSION;
63 	rp->type = mp->type;
64 	rp->id_num = htonl(0);
65 	if (mp->vers != TALK_VERSION) {
66 		syslog(LOG_WARNING, "Bad protocol version %d", mp->vers);
67 		rp->answer = BADVERSION;
68 		return;
69 	}
70 	mp->id_num = ntohl(mp->id_num);
71 	if (ntohs(mp->addr.sa_family) != AF_INET) {
72 		syslog(LOG_WARNING, "Bad address, family %d",
73 		    ntohs(mp->addr.sa_family));
74 		rp->answer = BADADDR;
75 		return;
76 	}
77 	if (ntohs(mp->ctl_addr.sa_family) != AF_INET) {
78 		syslog(LOG_WARNING, "Bad control address, family %d",
79 		    ntohs(mp->ctl_addr.sa_family));
80 		rp->answer = BADCTLADDR;
81 		return;
82 	}
83 	for (s = mp->l_name; *s; s++)
84 		if (!isprint((unsigned char)*s)) {
85 			syslog(LOG_NOTICE, "Illegal user name. Aborting");
86 			rp->answer = FAILED;
87 			return;
88 		}
89 	if (memcmp(&satosin(&rp->addr)->sin_addr,
90 	    &satosin(&mp->ctl_addr)->sin_addr,
91 	    sizeof(struct in_addr))) {
92 		char buf1[32], buf2[32];
93 
94 		strlcpy(buf1, inet_ntoa(satosin(&rp->addr)->sin_addr),
95 		    sizeof(buf1));
96 		strlcpy(buf2, inet_ntoa(satosin(&mp->ctl_addr)->sin_addr),
97 		    sizeof(buf2));
98 		syslog(LOG_WARNING, "addresses are different, %s != %s",
99 		    buf1, buf2);
100 	}
101 	rp->addr.sa_family = 0;
102 	mp->pid = ntohl(mp->pid);
103 	if (debug)
104 		print_request("process_request", mp);
105 	switch (mp->type) {
106 
107 	case ANNOUNCE:
108 		do_announce(mp, rp);
109 		break;
110 
111 	case LEAVE_INVITE:
112 		ptr = find_request(mp);
113 		if (ptr != (CTL_MSG *)0) {
114 			rp->id_num = htonl(ptr->id_num);
115 			rp->answer = SUCCESS;
116 		} else
117 			insert_table(mp, rp);
118 		break;
119 
120 	case LOOK_UP:
121 		ptr = find_match(mp);
122 		if (ptr != (CTL_MSG *)0) {
123 			rp->id_num = htonl(ptr->id_num);
124 			rp->addr = ptr->addr;
125 			rp->addr.sa_family = ptr->addr.sa_family;
126 			rp->answer = SUCCESS;
127 		} else
128 			rp->answer = NOT_HERE;
129 		break;
130 
131 	case DELETE:
132 		rp->answer = delete_invite(mp->id_num);
133 		break;
134 
135 	default:
136 		rp->answer = UNKNOWN_REQUEST;
137 		break;
138 	}
139 	if (debug)
140 		print_response("process_request", rp);
141 }
142 
143 void
144 do_announce(CTL_MSG *mp, CTL_RESPONSE *rp)
145 {
146 	struct hostent *hp;
147 	CTL_MSG *ptr;
148 	int result;
149 
150 	/* see if the user is logged */
151 	result = find_user(mp->r_name, mp->r_tty, sizeof(mp->r_tty));
152 	if (result != SUCCESS) {
153 		rp->answer = result;
154 		return;
155 	}
156 	hp = gethostbyaddr((char *)&satosin(&mp->ctl_addr)->sin_addr,
157 		sizeof(struct in_addr), AF_INET);
158 	if (hp == (struct hostent *)0) {
159 		rp->answer = MACHINE_UNKNOWN;
160 		return;
161 	}
162 	ptr = find_request(mp);
163 	if (ptr == (CTL_MSG *) 0) {
164 		insert_table(mp, rp);
165 		rp->answer = announce(mp, hp->h_name);
166 		return;
167 	}
168 	if (mp->id_num > ptr->id_num) {
169 		/*
170 		 * This is an explicit re-announce, so update the id_num
171 		 * field to avoid duplicates and re-announce the talk.
172 		 */
173 		ptr->id_num = new_id();
174 		rp->id_num = htonl(ptr->id_num);
175 		rp->answer = announce(mp, hp->h_name);
176 	} else {
177 		/* a duplicated request, so ignore it */
178 		rp->id_num = htonl(ptr->id_num);
179 		rp->answer = SUCCESS;
180 	}
181 }
182 
183 #include <utmp.h>
184 
185 /*
186  * Search utmp for the local user
187  */
188 int
189 find_user(char *name, char *tty, size_t ttyl)
190 {
191 	struct utmp ubuf, ubuf1;
192 	int status;
193 	FILE *fp;
194 	char line[UT_LINESIZE+1];
195 	char ftty[MAXPATHLEN];
196 	time_t	idle, now;
197 
198 	time(&now);
199 	idle = INT_MAX;
200 	if ((fp = fopen(_PATH_UTMP, "r")) == NULL) {
201 		fprintf(stderr, "talkd: can't read %s.\n", _PATH_UTMP);
202 		return (FAILED);
203 	}
204 #define SCMPN(a, b)	strncmp(a, b, sizeof(a))
205 	status = NOT_HERE;
206 	(void) strlcpy(ftty, _PATH_DEV, sizeof(ftty));
207 	while (fread((char *) &ubuf, sizeof(ubuf), 1, fp) == 1)
208 		if (SCMPN(ubuf.ut_name, name) == 0) {
209 			if (*tty == '\0') {
210 				/* no particular tty was requested */
211 				struct stat statb;
212 
213 				memcpy(line, ubuf.ut_line, UT_LINESIZE);
214 				line[sizeof(line)-1] = '\0';
215 				ftty[sizeof(_PATH_DEV)-1] = '\0';
216 				strlcat(ftty, line, sizeof(ftty));
217 				if (stat(ftty, &statb) == 0) {
218 					if (!(statb.st_mode & S_IWGRP)) {
219 						if (status == NOT_HERE)
220 							status = PERMISSION_DENIED;
221 					} else if (now - statb.st_atime < idle) {
222 						idle = now - statb.st_atime;
223 						status = SUCCESS;
224 						ubuf1 = ubuf;
225 					}
226 				}
227 			} else if (SCMPN(ubuf.ut_line, tty) == 0) {
228 				status = SUCCESS;
229 				break;
230 			}
231 		}
232 	fclose(fp);
233 	if (*tty == '\0' && status == SUCCESS) {
234 		memcpy(line, ubuf1.ut_line, UT_LINESIZE);
235 		line[sizeof(line)-1] = '\0';
236 		strlcpy(tty, line, ttyl);
237 	}
238 	return (status);
239 }
240