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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
22  * Use is subject to license terms.
23  */
24 
25 /*
26  * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
27  * All Rights Reserved.
28  */
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California.
33  * All Rights Reserved.
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 /*
41  * process.c handles the requests, which can be of three types:
42  *
43  * ANNOUNCE - announce to a user that a talk is wanted
44  *
45  * LEAVE_INVITE - insert the request into the table
46  *
47  * LOOK_UP - look up to see if a request is waiting in
48  * in the table for the local user
49  *
50  * DELETE - delete invitation
51  *
52  */
53 
54 #include <sys/types.h>
55 #include <sys/stat.h>
56 #include <fcntl.h>
57 #include <syslog.h>
58 #include <string.h>
59 #include <utmpx.h>
60 #include <unistd.h>
61 #include <stdlib.h>
62 #include <stdio.h>
63 #include "talkd_impl.h"
64 
65 static void do_announce(CTL_MSG *request, CTL_RESPONSE *response);
66 static int find_user(char *name, char *tty);
67 
68 void
69 process_request(CTL_MSG *request, CTL_RESPONSE *response)
70 {
71 	CTL_MSG *ptr;
72 
73 	response->type = request->type;
74 	response->id_num = 0;
75 
76 	/*
77 	 * Check if any of the strings within the request structure aren't
78 	 * NUL terminated, and if so don't bother processing the request
79 	 * further.
80 	 */
81 	if ((memchr(request->l_name, '\0', sizeof (request->l_name)) == NULL) ||
82 	    (memchr(request->r_name, '\0', sizeof (request->r_name)) == NULL) ||
83 	    (memchr(request->r_tty, '\0', sizeof (request->r_tty)) == NULL)) {
84 		response->answer = FAILED;
85 		openlog("talk", 0, LOG_AUTH);
86 		syslog(LOG_CRIT, "malformed talk request\n");
87 		closelog();
88 		return;
89 	}
90 
91 	switch (request->type) {
92 
93 	    case ANNOUNCE :
94 
95 		do_announce(request, response);
96 		break;
97 
98 	    case LEAVE_INVITE :
99 
100 		ptr = find_request(request);
101 		if (ptr != NULL) {
102 			response->id_num = ptr->id_num;
103 			response->answer = SUCCESS;
104 		} else {
105 			insert_table(request, response);
106 		}
107 		break;
108 
109 	    case LOOK_UP :
110 
111 		ptr = find_match(request);
112 		if (ptr != NULL) {
113 			response->id_num = ptr->id_num;
114 			response->addr = ptr->addr;
115 			response->answer = SUCCESS;
116 		} else {
117 			response->answer = NOT_HERE;
118 		}
119 		break;
120 
121 	    case DELETE :
122 
123 		response->answer = delete_invite(request->id_num);
124 		break;
125 
126 	    default :
127 
128 		response->answer = UNKNOWN_REQUEST;
129 		break;
130 	}
131 }
132 
133 static void
134 do_announce(CTL_MSG *request, CTL_RESPONSE *response)
135 {
136 	struct hostent *hp;
137 	CTL_MSG *ptr;
138 	int result;
139 
140 	/*
141 	 * See if the user is logged.
142 	 */
143 	result = find_user(request->r_name, request->r_tty);
144 	if (result != SUCCESS) {
145 		response->answer = result;
146 		return;
147 	}
148 
149 	hp = gethostbyaddr((const char *)&request->ctl_addr.sin_addr,
150 	    sizeof (struct in_addr), AF_INET);
151 	if (hp == NULL) {
152 		response->answer = MACHINE_UNKNOWN;
153 		return;
154 	}
155 
156 	ptr = find_request(request);
157 	if (ptr == NULL) {
158 		insert_table(request, response);
159 		response->answer = announce(request, hp->h_name);
160 	} else if (request->id_num > ptr->id_num) {
161 		/*
162 		 * This is an explicit re-announce, so update the id_num
163 		 * field to avoid duplicates and re-announce the talk.
164 		 */
165 		ptr->id_num = response->id_num = new_id();
166 		response->answer = announce(request, hp->h_name);
167 	} else {
168 		/* a duplicated request, so ignore it */
169 		response->id_num = ptr->id_num;
170 		response->answer = SUCCESS;
171 	}
172 }
173 
174 /*
175  * Search utmp for the local user.
176  */
177 
178 static int
179 find_user(char *name, char *tty)
180 {
181 	struct utmpx *ubuf;
182 	int tfd;
183 	char dev[MAXPATHLEN];
184 	struct stat stbuf;
185 	int problem = NOT_HERE;
186 
187 	setutxent();		/* reset the utmpx file */
188 
189 	while (ubuf = getutxent()) {
190 		if (ubuf->ut_type == USER_PROCESS &&
191 		    strncmp(ubuf->ut_user, name, sizeof (ubuf->ut_user)) == 0) {
192 			/*
193 			 * Check if this entry is really a tty.
194 			 */
195 			(void) snprintf(dev, sizeof (dev), "/dev/%.*s",
196 			    sizeof (ubuf->ut_line), ubuf->ut_line);
197 			if ((tfd = open(dev, O_WRONLY|O_NOCTTY)) == -1) {
198 				continue;
199 			}
200 			if (!isatty(tfd)) {
201 				(void) close(tfd);
202 				openlog("talk", 0, LOG_AUTH);
203 				syslog(LOG_CRIT, "%.*s in utmp is not a tty\n",
204 				    sizeof (ubuf->ut_line), ubuf->ut_line);
205 				closelog();
206 				continue;
207 			}
208 			if (*tty == '\0') {
209 				/*
210 				 * No particular tty was requested.
211 				 */
212 				if (fstat(tfd, &stbuf) < 0 ||
213 				    (stbuf.st_mode&020) == 0) {
214 					(void) close(tfd);
215 					problem = PERMISSION_DENIED;
216 					continue;
217 				}
218 				(void) close(tfd);
219 				(void) strlcpy(tty, ubuf->ut_line, TTY_SIZE);
220 				endutxent();	/* close the utmpx file */
221 				return (SUCCESS);
222 			}
223 			(void) close(tfd);
224 			if (strcmp(ubuf->ut_line, tty) == 0) {
225 				endutxent();	/* close the utmpx file */
226 				return (SUCCESS);
227 			}
228 		}
229 	}
230 
231 	endutxent();		/* close the utmpx file */
232 	return (problem);
233 }
234