*** empty log message ***
[heimdal.git] / appl / kx / kxd.c
blob6bfcb2156663eed59a84cef95f8e96c456351678
1 /*
2 * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the Kungliga Tekniska
20 * Högskolan and its contributors.
22 * 4. Neither the name of the Institute nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
39 #include "kx.h"
41 RCSID("$Id$");
44 * Signal handler that justs waits for the children when they die.
47 static RETSIGTYPE
48 childhandler (int sig)
50 pid_t pid;
51 int status;
53 do {
54 pid = waitpid (-1, &status, WNOHANG|WUNTRACED);
55 } while(pid > 0);
56 signal (SIGCHLD, childhandler);
57 SIGRETURN(0);
60 static void
61 fatal(int, des_cblock *, des_key_schedule,
62 struct sockaddr_in *, struct sockaddr_in *,
63 char *format, ...)
64 #ifdef __GNUC__
65 __attribute__ ((format (printf, 6, 7)))
66 #endif
69 static void
70 fatal (int fd, des_cblock *key, des_key_schedule schedule,
71 struct sockaddr_in *thisaddr,
72 struct sockaddr_in *thataddr,
73 char *format, ...)
75 u_char msg[1024];
76 u_char *p;
77 va_list args;
78 int len;
80 va_start(args, format);
81 p = msg;
82 *p++ = ERROR;
83 vsnprintf (p + 4, sizeof(msg) - 5, format, args);
84 syslog (LOG_ERR, p + 4);
85 len = strlen (p + 4);
86 p += krb_put_int (len, p, 4);
87 p += len;
88 write_encrypted (fd, msg, p - msg, schedule, key, thisaddr, thataddr);
89 va_end(args);
90 exit (1);
93 static void
94 cleanup(void)
96 if(xauthfile[0])
97 unlink(xauthfile);
98 if(x_socket[0])
99 unlink(x_socket);
102 static int
103 recv_conn (int sock, des_cblock *key, des_key_schedule schedule,
104 struct sockaddr_in *thisaddr,
105 struct sockaddr_in *thataddr)
107 int status;
108 KTEXT_ST ticket;
109 AUTH_DAT auth;
110 char user[ANAME_SZ + 1];
111 char instance[INST_SZ + 1];
112 int addrlen;
113 char version[KRB_SENDAUTH_VLEN + 1];
114 struct passwd *passwd;
115 char remotehost[MaxHostNameLen];
116 void *ret;
117 int len;
118 u_char msg[1024], *p;
119 u_int32_t tmp;
120 int flags;
122 addrlen = sizeof(*thisaddr);
123 if (getsockname (sock, (struct sockaddr *)thisaddr, &addrlen) < 0 ||
124 addrlen != sizeof(*thisaddr)) {
125 syslog (LOG_ERR, "getsockname: %m");
126 exit (1);
128 addrlen = sizeof(*thataddr);
129 if (getpeername (sock, (struct sockaddr *)thataddr, &addrlen) < 0 ||
130 addrlen != sizeof(*thataddr)) {
131 syslog (LOG_ERR, "getpeername: %m");
132 exit (1);
135 inaddr2str (thataddr->sin_addr, remotehost, sizeof(remotehost));
137 k_getsockinst (sock, instance, sizeof(instance));
138 status = krb_recvauth (KOPT_DO_MUTUAL, sock, &ticket, "rcmd", instance,
139 thataddr, thisaddr, &auth, "", schedule,
140 version);
141 if (status != KSUCCESS) {
142 syslog (LOG_ERR, "krb_recvauth: %s",
143 krb_get_err_text(status));
144 exit(1);
146 if( strncmp(version, KX_VERSION, KRB_SENDAUTH_VLEN) != 0) {
147 /* Try to be nice to old kx's */
148 if (strncmp (version, KX_OLD_VERSION, KRB_SENDAUTH_VLEN) == 0) {
149 char *old_errmsg = "\001Old version of kx. Please upgrade.";
151 syslog (LOG_ERR, "Old version client (%s)", version);
153 krb_net_read (sock, user, sizeof(user));
154 krb_net_write (sock, old_errmsg, strlen(old_errmsg) + 1);
155 exit (1);
158 fatal(sock, key, schedule, thisaddr, thataddr,
159 "Bad version %s", version);
161 memcpy(key, &auth.session, sizeof(des_cblock));
163 len = read_encrypted (sock, msg, sizeof(msg), &ret,
164 schedule, key, thataddr, thisaddr);
165 if (len < 0)
166 return 1;
167 p = (u_char *)ret;
168 if (*p != INIT)
169 fatal(sock, key, schedule, thisaddr, thataddr,
170 "Bad message");
171 p++;
172 p += krb_get_int (p, &tmp, 4, 0);
173 len = min(sizeof(user), tmp);
174 strncpy (user, p, len);
175 p += tmp;
176 user[len] = '\0';
178 passwd = k_getpwnam (user);
179 if (passwd == NULL)
180 fatal (sock, key, schedule, thisaddr, thataddr,
181 "Cannot find uid");
182 if (kuserok(&auth, user) != 0)
183 fatal (sock, key, schedule, thisaddr, thataddr,
184 "%s is not allowed to login as %s",
185 krb_unparse_name_long (auth.pname,
186 auth.pinst,
187 auth.prealm),
188 user);
189 if (setgid (passwd->pw_gid) ||
190 initgroups(passwd->pw_name, passwd->pw_gid) ||
191 setuid(passwd->pw_uid)) {
192 fatal (sock, key, schedule, thisaddr, thataddr,
193 "Cannot set uid");
195 syslog (LOG_INFO, "from %s(%s): %s -> %s",
196 remotehost,
197 inet_ntoa(thataddr->sin_addr),
198 krb_unparse_name_long (auth.pname, auth.pinst, auth.prealm),
199 user);
200 umask(077);
201 flags = *p++;
202 if (!(flags & PASSIVE)) {
203 p += krb_get_int (p, &tmp, 4, 0);
204 len = min(tmp, display_size);
205 strncpy (display, p, len);
206 display[len] = '\0';
207 p += tmp;
208 p += krb_get_int (p, &tmp, 4, 0);
209 len = min(tmp, xauthfile_size);
210 strncpy (xauthfile, p, len);
211 p += tmp;
213 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
214 if (flags & KEEP_ALIVE) {
215 int one = 1;
217 setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
218 sizeof(one));
220 #endif
221 return flags;
228 static int
229 passive_session (int fd, int sock, des_cblock *key,
230 des_key_schedule schedule)
232 if (verify_and_remove_cookies (fd, sock))
233 return 1;
234 else
235 return copy_encrypted (fd, sock, key, schedule);
238 static int
239 active_session (int fd, int sock, des_cblock *key,
240 des_key_schedule schedule)
242 fd = connect_local_xsocket(0);
244 if (replace_cookie (fd, sock, xauthfile))
245 return 1;
246 else
247 return copy_encrypted (fd, sock, key, schedule);
250 static int
251 doit_conn (int fd, int meta_sock, int flags,
252 des_cblock *key, des_key_schedule schedule,
253 struct sockaddr_in *thisaddr,
254 struct sockaddr_in *thataddr)
256 int sock, sock2;
257 struct sockaddr_in addr;
258 int addrlen;
259 u_char msg[1024], *p;
261 sock = socket (AF_INET, SOCK_STREAM, 0);
262 if (sock < 0) {
263 syslog (LOG_ERR, "socket: %m");
264 return 1;
266 #if defined(TCP_NODELAY) && defined(HAVE_SETSOCKOPT)
268 int one = 1;
269 setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (void *)&one, sizeof(one));
271 #endif
272 #if defined(SO_KEEPALIVE) && defined(HAVE_SETSOCKOPT)
273 if (flags & KEEP_ALIVE) {
274 int one = 1;
276 setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&one,
277 sizeof(one));
279 #endif
280 memset (&addr, 0, sizeof(addr));
281 addr.sin_family = AF_INET;
282 if (bind (sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
283 syslog (LOG_ERR, "bind: %m");
284 return 1;
286 addrlen = sizeof(addr);
287 if (getsockname (sock, (struct sockaddr *)&addr,
288 &addrlen) < 0) {
289 syslog (LOG_ERR, "getsockname: %m");
290 return 1;
292 if (listen (sock, SOMAXCONN) < 0) {
293 syslog (LOG_ERR, "listen: %m");
294 return 1;
296 p = msg;
297 *p++ = NEW_CONN;
298 p += krb_put_int (ntohs(addr.sin_port), p, 4);
300 if (write_encrypted (meta_sock, msg, p - msg, schedule, key,
301 thisaddr, thataddr) < 0) {
302 syslog (LOG_ERR, "write: %m");
303 return 1;
306 sock2 = accept (sock, (struct sockaddr *)thisaddr, &addrlen);
307 if (sock2 < 0) {
308 syslog (LOG_ERR, "accept: %m");
309 return 1;
311 close (sock);
312 close (meta_sock);
314 if (flags & PASSIVE)
315 return passive_session (fd, sock2, key, schedule);
316 else
317 return active_session (fd, sock2, key, schedule);
324 static void
325 check_user_console (int fd, des_cblock *key, des_key_schedule schedule,
326 struct sockaddr_in *thisaddr,
327 struct sockaddr_in *thataddr)
329 struct stat sb;
331 if (stat ("/dev/console", &sb) < 0)
332 fatal (fd, key, schedule, thisaddr, thataddr,
333 "Cannot stat /dev/console");
334 if (getuid() != sb.st_uid)
335 fatal (fd, key, schedule, thisaddr, thataddr,
336 "Permission denied");
340 * Receive a connection on `sock' and process it.
343 static int
344 doit(int sock, int tcpp)
346 des_key_schedule schedule;
347 des_cblock key;
348 int localx, tcpx;
349 struct sockaddr_in me, him;
350 int flags;
351 u_char msg[1024], *p;
353 flags = recv_conn (sock, &key, schedule, &me, &him);
355 if (flags & PASSIVE) {
356 int tmp;
357 int len;
359 tmp = get_xsockets (&localx, tcpp ? &tcpx : NULL);
360 if (tmp < 0)
361 return 1;
362 display_num = tmp;
363 if (tcpp)
364 snprintf (display, display_size, "localhost:%u", display_num);
365 else
366 snprintf (display, display_size, ":%u", display_num);
367 if(create_and_write_cookie (xauthfile, xauthfile_size,
368 cookie, cookie_len)) {
369 syslog(LOG_ERR, "create_and_write_cookie: %m");
370 fatal (sock, &key, schedule, &me, &him,
371 "Cookie-creation failed with: %s",
372 strerror(errno));
373 cleanup();
374 return 1;
377 p = msg;
378 *p++ = ACK;
379 len = strlen (display);
380 p += krb_put_int (len, p, 4);
381 strncpy (p, display, len);
382 p += len;
383 len = strlen (xauthfile);
384 p += krb_put_int (len, p, 4);
385 strncpy (p, xauthfile, len);
386 p += len;
388 if(write_encrypted (sock, msg, p - msg, schedule, &key,
389 &me, &him) < 0) {
390 syslog (LOG_ERR, "write: %m");
391 cleanup();
392 return 1;
394 for (;;) {
395 pid_t child;
396 int fd;
397 int zero = 0;
398 fd_set fds;
400 FD_ZERO(&fds);
401 FD_SET(localx, &fds);
402 FD_SET(sock, &fds);
403 if (tcpp)
404 FD_SET(tcpx, &fds);
405 if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) <=0)
406 continue;
407 if(FD_ISSET(sock, &fds)){
408 /* there are no processes left on the remote side
410 cleanup();
411 exit(0);
412 } else if(FD_ISSET(localx, &fds))
413 fd = accept (localx, NULL, &zero);
414 else if(tcpp && FD_ISSET(tcpx, &fds)) {
415 struct sockaddr_in peer;
416 int len = sizeof(peer);
418 fd = accept (tcpx, (struct sockaddr *)&peer, &len);
419 /* XXX */
420 if (fd >= 0 && suspicious_address (fd, peer)) {
421 close (fd);
422 continue;
424 } else
425 continue;
426 if (fd < 0)
427 if (errno == EINTR)
428 continue;
429 else {
430 syslog (LOG_ERR, "accept: %m");
431 return 1;
434 child = fork ();
435 if (child < 0) {
436 syslog (LOG_ERR, "fork: %m");
437 return 1;
438 } else if (child == 0) {
439 close (localx);
440 if (tcpp)
441 close (tcpx);
442 return doit_conn (fd, sock, flags,
443 &key, schedule, &me, &him);
444 } else {
445 close (fd);
448 } else {
449 check_user_console (sock, &key, schedule, &me, &him);
451 p = msg;
452 *p++ = ACK;
454 if(write_encrypted (sock, msg, p - msg, schedule, &key,
455 &me, &him) < 0) {
456 syslog (LOG_ERR, "write: %m");
457 return 1;
459 for (;;) {
460 pid_t child;
461 int len;
462 void *ret;
464 len = read_encrypted (sock, msg, sizeof(msg), &ret,
465 schedule, &key,
466 &him, &me);
467 if (len < 0) {
468 syslog (LOG_ERR, "read: %m");
469 return 1;
471 p = (u_char *)ret;
472 if (*p != NEW_CONN) {
473 syslog (LOG_ERR, "bad_message: %d", *p);
474 return 1;
477 child = fork ();
478 if (child < 0) {
479 syslog (LOG_ERR, "fork: %m");
480 return 1;
481 } else if (child == 0) {
482 return doit_conn (localx, sock, flags,
483 &key, schedule, &me, &him);
484 } else {
490 static void
491 usage (void)
493 fprintf (stderr, "Usage: %s [-i] [-t] [-p port]\n", __progname);
494 exit (1);
498 * kxd - receive a forwarded X conncection
502 main (int argc, char **argv)
504 int c;
505 int no_inetd = 0;
506 int tcpp = 0;
507 int port = 0;
509 set_progname (argv[0]);
511 while( (c = getopt (argc, argv, "itp:")) != EOF) {
512 switch (c) {
513 case 'i':
514 no_inetd = 1;
515 break;
516 case 't':
517 tcpp = 1;
518 break;
519 case 'p':
520 port = htons(atoi (optarg));
521 break;
522 case '?':
523 default:
524 usage ();
528 if (no_inetd)
529 mini_inetd (port ? port : k_getportbyname("kx", "tcp",
530 htons(KX_PORT)));
531 openlog(__progname, LOG_PID|LOG_CONS, LOG_DAEMON);
532 signal (SIGCHLD, childhandler);
533 return doit(STDIN_FILENO, tcpp);