1 /* $NetBSD: rcmd.c,v 1.71 2014/11/26 23:44:21 enami Exp $ */
2
3 /*
4 * Copyright (c) 1983, 1993, 1994
5 * The Regents of the University of California. 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 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 #if 0
35 static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94";
36 #else
37 __RCSID("$NetBSD: rcmd.c,v 1.71 2014/11/26 23:44:21 enami Exp $");
38 #endif
39 #endif /* LIBC_SCCS and not lint */
40
41 #ifdef _LIBC
42 #include "namespace.h"
43 #endif
44 #include <sys/param.h>
45 #include <sys/socket.h>
46 #include <sys/stat.h>
47 #include <sys/poll.h>
48 #include <sys/wait.h>
49
50 #include <netinet/in.h>
51 #if !defined(__minix)
52 #include <rpc/rpc.h>
53 #endif /* !defined(__minix) */
54 #include <arpa/inet.h>
55 #include <netgroup.h>
56
57 #include <assert.h>
58 #include <ctype.h>
59 #include <err.h>
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <grp.h>
63 #include <netdb.h>
64 #include <paths.h>
65 #include <pwd.h>
66 #include <signal.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <syslog.h>
71 #include <unistd.h>
72
73 #include "pathnames.h"
74
75 int orcmd(char **, u_int, const char *, const char *, const char *, int *);
76 int orcmd_af(char **, u_int, const char *, const char *, const char *,
77 int *, int);
78 int __ivaliduser(FILE *, u_int32_t, const char *, const char *);
79 int __ivaliduser_sa(FILE *, const struct sockaddr *, socklen_t,
80 const char *, const char *);
81 static int rshrcmd(int, char **, u_int32_t, const char *,
82 const char *, const char *, int *, const char *);
83 static int resrcmd(struct addrinfo *, char **, u_int32_t, const char *,
84 const char *, const char *, int *);
85 static int __icheckhost(const struct sockaddr *, socklen_t,
86 const char *);
87 static char *__gethostloop(const struct sockaddr *, socklen_t);
88
89 int
rcmd(char ** ahost,int rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p)90 rcmd(char **ahost, int rport, const char *locuser, const char *remuser,
91 const char *cmd, int *fd2p)
92 {
93
94 return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
95 }
96
97 int
rcmd_af(char ** ahost,int rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p,int af)98 rcmd_af(char **ahost, int rport, const char *locuser, const char *remuser,
99 const char *cmd, int *fd2p, int af)
100 {
101 static char hbuf[MAXHOSTNAMELEN];
102 char pbuf[NI_MAXSERV];
103 struct addrinfo hints, *res;
104 int error;
105 struct servent *sp;
106
107 _DIAGASSERT(ahost != NULL);
108 _DIAGASSERT(locuser != NULL);
109 _DIAGASSERT(remuser != NULL);
110 _DIAGASSERT(cmd != NULL);
111 /* fd2p may be NULL */
112
113 snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport));
114 memset(&hints, 0, sizeof(hints));
115 hints.ai_family = af;
116 hints.ai_socktype = SOCK_STREAM;
117 hints.ai_flags = AI_CANONNAME;
118 error = getaddrinfo(*ahost, pbuf, &hints, &res);
119 if (error) {
120 warnx("%s: %s", *ahost, gai_strerror(error)); /*XXX*/
121 return -1;
122 }
123 if (res->ai_canonname) {
124 /*
125 * Canonicalise hostname.
126 * XXX: Should we really do this?
127 */
128 strlcpy(hbuf, res->ai_canonname, sizeof(hbuf));
129 *ahost = hbuf;
130 }
131
132 /*
133 * Check if rport is the same as the shell port, and that the fd2p. If
134 * it is not, the program isn't expecting 'rsh' and so we can't use the
135 * RCMD_CMD environment.
136 */
137 sp = getservbyname("shell", "tcp");
138 if (sp != NULL && sp->s_port == rport)
139 error = rshrcmd(af, ahost, (u_int32_t)rport,
140 locuser, remuser, cmd, fd2p, getenv("RCMD_CMD"));
141 else
142 error = resrcmd(res, ahost, (u_int32_t)rport,
143 locuser, remuser, cmd, fd2p);
144 freeaddrinfo(res);
145 return error;
146 }
147
148 /* this is simply a wrapper around hprcmd() that handles ahost first */
149 int
orcmd(char ** ahost,u_int rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p)150 orcmd(char **ahost, u_int rport, const char *locuser, const char *remuser,
151 const char *cmd, int *fd2p)
152 {
153 return orcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
154 }
155
156 int
orcmd_af(char ** ahost,u_int rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p,int af)157 orcmd_af(char **ahost, u_int rport, const char *locuser, const char *remuser,
158 const char *cmd, int *fd2p, int af)
159 {
160 static char hbuf[MAXHOSTNAMELEN];
161 char pbuf[NI_MAXSERV];
162 struct addrinfo hints, *res;
163 int error;
164
165 _DIAGASSERT(ahost != NULL);
166 _DIAGASSERT(locuser != NULL);
167 _DIAGASSERT(remuser != NULL);
168 _DIAGASSERT(cmd != NULL);
169 /* fd2p may be NULL */
170
171 snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport));
172 memset(&hints, 0, sizeof(hints));
173 hints.ai_family = af;
174 hints.ai_socktype = SOCK_STREAM;
175 hints.ai_flags = AI_CANONNAME;
176 error = getaddrinfo(*ahost, pbuf, &hints, &res);
177 if (error) {
178 warnx("%s: %s", *ahost, gai_strerror(error)); /*XXX*/
179 return -1;
180 }
181 if (res->ai_canonname) {
182 strlcpy(hbuf, res->ai_canonname, sizeof(hbuf));
183 *ahost = hbuf;
184 }
185
186 error = resrcmd(res, ahost, rport, locuser, remuser, cmd, fd2p);
187 freeaddrinfo(res);
188 return error;
189 }
190
191 /*ARGSUSED*/
192 static int
resrcmd(struct addrinfo * res,char ** ahost,u_int32_t rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p)193 resrcmd(struct addrinfo *res, char **ahost, u_int32_t rport,
194 const char *locuser, const char *remuser, const char *cmd, int *fd2p)
195 {
196 struct addrinfo *r;
197 struct sockaddr_storage from;
198 struct pollfd reads[2];
199 sigset_t nmask, omask;
200 pid_t pid;
201 int s, lport, timo;
202 int pollr;
203 char c;
204 int refused;
205
206 _DIAGASSERT(res != NULL);
207 _DIAGASSERT(ahost != NULL);
208 _DIAGASSERT(locuser != NULL);
209 _DIAGASSERT(remuser != NULL);
210 _DIAGASSERT(cmd != NULL);
211 /* fd2p may be NULL */
212
213 r = res;
214 refused = 0;
215 pid = getpid();
216 sigemptyset(&nmask);
217 sigaddset(&nmask, SIGURG);
218 if (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1)
219 return -1;
220 for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
221 s = rresvport_af(&lport, r->ai_family);
222 if (s < 0) {
223 if (errno == EAGAIN)
224 warnx("rcmd: socket: All ports in use");
225 else
226 warn("rcmd: socket");
227 if (r->ai_next) {
228 r = r->ai_next;
229 continue;
230 } else {
231 (void)sigprocmask(SIG_SETMASK, &omask, NULL);
232 return -1;
233 }
234 }
235 fcntl(s, F_SETOWN, pid);
236 if (connect(s, r->ai_addr, r->ai_addrlen) >= 0)
237 break;
238 (void)close(s);
239 if (errno == EADDRINUSE) {
240 lport--;
241 continue;
242 } else if (errno == ECONNREFUSED)
243 refused++;
244 if (r->ai_next) {
245 int oerrno = errno;
246 char hbuf[NI_MAXHOST];
247 const int niflags = NI_NUMERICHOST;
248
249 hbuf[0] = '\0';
250 if (getnameinfo(r->ai_addr, r->ai_addrlen,
251 hbuf, (socklen_t)sizeof(hbuf), NULL, 0, niflags) !=
252 0)
253 strlcpy(hbuf, "(invalid)", sizeof(hbuf));
254 errno = oerrno;
255 warn("rcmd: connect to address %s", hbuf);
256 r = r->ai_next;
257 hbuf[0] = '\0';
258 if (getnameinfo(r->ai_addr, r->ai_addrlen,
259 hbuf, (socklen_t)sizeof(hbuf), NULL, 0, niflags) !=
260 0)
261 strlcpy(hbuf, "(invalid)", sizeof(hbuf));
262 (void)fprintf(stderr, "Trying %s...\n", hbuf);
263 continue;
264 }
265 if (refused && timo <= 16) {
266 (void)sleep((unsigned int)timo);
267 timo *= 2;
268 r = res;
269 refused = 0;
270 continue;
271 }
272 (void)fprintf(stderr, "%s: %s\n", res->ai_canonname,
273 strerror(errno));
274 (void)sigprocmask(SIG_SETMASK, &omask, NULL);
275 return -1;
276 }
277 lport--;
278 if (fd2p == 0) {
279 write(s, "", 1);
280 lport = 0;
281 } else {
282 char num[8];
283 int s2 = rresvport_af(&lport, r->ai_family), s3;
284 socklen_t len = sizeof(from);
285
286 if (s2 < 0)
287 goto bad;
288 listen(s2, 1);
289 (void)snprintf(num, sizeof(num), "%d", lport);
290 if (write(s, num, strlen(num) + 1) !=
291 (ssize_t) (strlen(num) + 1)) {
292 warn("rcmd: write (setting up stderr)");
293 (void)close(s2);
294 goto bad;
295 }
296 reads[0].fd = s;
297 reads[0].events = POLLIN;
298 reads[1].fd = s2;
299 reads[1].events = POLLIN;
300 errno = 0;
301 pollr = poll(reads, 2, INFTIM);
302 if (pollr < 1 || (reads[1].revents & POLLIN) == 0) {
303 if (errno != 0)
304 warn("poll: setting up stderr");
305 else
306 warnx(
307 "poll: protocol failure in circuit setup");
308 (void)close(s2);
309 goto bad;
310 }
311 s3 = accept(s2, (struct sockaddr *)(void *)&from, &len);
312 (void)close(s2);
313 if (s3 < 0) {
314 warn("rcmd: accept");
315 lport = 0;
316 goto bad;
317 }
318 *fd2p = s3;
319 switch (((struct sockaddr *)(void *)&from)->sa_family) {
320 case AF_INET:
321 #ifdef INET6
322 case AF_INET6:
323 #endif
324 if (getnameinfo((struct sockaddr *)(void *)&from, len,
325 NULL, 0, num, (socklen_t)sizeof(num),
326 NI_NUMERICSERV) != 0 ||
327 (atoi(num) >= IPPORT_RESERVED ||
328 atoi(num) < IPPORT_RESERVED / 2)) {
329 warnx(
330 "rcmd: protocol failure in circuit setup.");
331 goto bad2;
332 }
333 break;
334 default:
335 break;
336 }
337 }
338
339 (void)write(s, locuser, strlen(locuser)+1);
340 (void)write(s, remuser, strlen(remuser)+1);
341 (void)write(s, cmd, strlen(cmd)+1);
342 if (read(s, &c, 1) != 1) {
343 warn("%s", *ahost);
344 goto bad2;
345 }
346 if (c != 0) {
347 while (read(s, &c, 1) == 1) {
348 (void)write(STDERR_FILENO, &c, 1);
349 if (c == '\n')
350 break;
351 }
352 goto bad2;
353 }
354 (void)sigprocmask(SIG_SETMASK, &omask, NULL);
355 return s;
356 bad2:
357 if (lport)
358 (void)close(*fd2p);
359 bad:
360 (void)close(s);
361 (void)sigprocmask(SIG_SETMASK, &omask, NULL);
362 return -1;
363 }
364
365 /*
366 * based on code written by Chris Siebenmann <cks@utcc.utoronto.ca>
367 */
368 /* ARGSUSED */
369 static int
rshrcmd(int af,char ** ahost,u_int32_t rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p,const char * rshcmd)370 rshrcmd(int af, char **ahost, u_int32_t rport, const char *locuser,
371 const char *remuser, const char *cmd, int *fd2p, const char *rshcmd)
372 {
373 pid_t pid;
374 int sp[2], ep[2];
375 char *p;
376 struct passwd *pw, pwres;
377 char pwbuf[1024];
378
379 _DIAGASSERT(ahost != NULL);
380 _DIAGASSERT(locuser != NULL);
381 _DIAGASSERT(remuser != NULL);
382 _DIAGASSERT(cmd != NULL);
383 /* fd2p may be NULL */
384
385 /* What rsh/shell to use. */
386 if (rshcmd == NULL)
387 rshcmd = _PATH_BIN_RCMD;
388
389 /* locuser must exist on this host. */
390 if (getpwnam_r(locuser, &pwres, pwbuf, sizeof(pwbuf), &pw) != 0 ||
391 pw == NULL) {
392 warnx("%s: unknown user: %s", __func__, locuser);
393 return -1;
394 }
395
396 /* get a socketpair we'll use for stdin and stdout. */
397 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sp) < 0) {
398 warn("%s: socketpair", __func__);
399 return -1;
400 }
401 /* we will use this for the fd2 pointer */
402 if (fd2p) {
403 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, ep) < 0) {
404 warn("%s: socketpair", __func__);
405 return -1;
406 }
407 *fd2p = ep[0];
408 }
409
410 pid = fork();
411 if (pid < 0) {
412 warn("%s: fork", __func__);
413 return -1;
414 }
415 if (pid == 0) {
416 /*
417 * child
418 * - we use sp[1] to be stdin/stdout, and close sp[0]
419 * - with fd2p, we use ep[1] for stderr, and close ep[0]
420 */
421 (void)close(sp[0]);
422 if (dup2(sp[1], 0) < 0 || dup2(0, 1) < 0) {
423 warn("%s: dup2", __func__);
424 _exit(1);
425 }
426 (void)close(sp[1]);
427 if (fd2p) {
428 if (dup2(ep[1], 2) < 0) {
429 warn("%s: dup2", __func__);
430 _exit(1);
431 }
432 (void)close(ep[0]);
433 (void)close(ep[1]);
434 } else if (dup2(0, 2) < 0) {
435 warn("%s: dup2", __func__);
436 _exit(1);
437 }
438 /* fork again to lose parent. */
439 pid = fork();
440 if (pid < 0) {
441 warn("%s: second fork", __func__);
442 _exit(1);
443 }
444 if (pid > 0)
445 _exit(0);
446
447 /* Orphan. Become local user for rshprog. */
448 if (setuid(pw->pw_uid)) {
449 warn("%s: setuid(%lu)", __func__, (u_long)pw->pw_uid);
450 _exit(1);
451 }
452
453 /*
454 * If we are rcmd'ing to "localhost" as the same user as we
455 * are, then avoid running remote shell for efficiency.
456 */
457 if (strcmp(*ahost, "localhost") == 0 &&
458 strcmp(locuser, remuser) == 0) {
459 if (pw->pw_shell[0] == '\0')
460 rshcmd = _PATH_BSHELL;
461 else
462 rshcmd = pw->pw_shell;
463 p = strrchr(rshcmd, '/');
464 execlp(rshcmd, p ? p + 1 : rshcmd, "-c", cmd, NULL);
465 } else {
466 const char *program;
467 program = strrchr(rshcmd, '/');
468 program = program ? program + 1 : rshcmd;
469 if (fd2p)
470 /* ask rcmd to relay signal information */
471 setenv("RCMD_RELAY_SIGNAL", "YES", 1);
472 switch (af) {
473 case AF_INET:
474 execlp(rshcmd, program, "-4", "-l", remuser,
475 *ahost, cmd, NULL);
476 break;
477
478 case AF_INET6:
479 execlp(rshcmd, program, "-6", "-l", remuser,
480 *ahost, cmd, NULL);
481 break;
482
483 default:
484 /* typically AF_UNSPEC, plus whatever */
485 execlp(rshcmd, program, "-l", remuser,
486 *ahost, cmd, NULL);
487 break;
488 }
489 }
490 warn("%s: exec %s", __func__, rshcmd);
491 _exit(1);
492 }
493 /* Parent */
494 (void)close(sp[1]);
495 if (fd2p)
496 (void)close(ep[1]);
497
498 (void)waitpid(pid, NULL, 0);
499 return sp[0];
500 }
501
502 int
rresvport(int * alport)503 rresvport(int *alport)
504 {
505
506 _DIAGASSERT(alport != NULL);
507
508 return rresvport_af(alport, AF_INET);
509 }
510
511 int
rresvport_af(int * alport,int family)512 rresvport_af(int *alport, int family)
513 {
514 return rresvport_af_addr(alport, family, NULL);
515 }
516
517 int
rresvport_af_addr(int * alport,int family,void * addr)518 rresvport_af_addr(int *alport, int family, void *addr)
519 {
520 struct sockaddr_storage ss;
521 struct sockaddr *sa;
522 socklen_t salen;
523 int s;
524 u_int16_t *portp;
525
526 _DIAGASSERT(alport != NULL);
527
528 memset(&ss, 0, sizeof(ss));
529 sa = (struct sockaddr *)(void *)&ss;
530 switch (family) {
531 case AF_INET:
532 #ifdef BSD4_4
533 sa->sa_len =
534 #endif
535 salen = sizeof(struct sockaddr_in);
536 if (addr)
537 ((struct sockaddr_in *)(void *)sa)->sin_addr =
538 ((struct sockaddr_in *)addr)->sin_addr;
539 portp = &((struct sockaddr_in *)(void *)sa)->sin_port;
540 break;
541 #ifdef INET6
542 case AF_INET6:
543 #ifdef BSD4_4
544 sa->sa_len =
545 #endif
546 salen = sizeof(struct sockaddr_in6);
547 if (addr)
548 ((struct sockaddr_in6 *)(void *)sa)->sin6_addr =
549 ((struct sockaddr_in6 *)addr)->sin6_addr;
550 portp = &((struct sockaddr_in6 *)(void *)sa)->sin6_port;
551 break;
552 #endif
553 default:
554 errno = EAFNOSUPPORT;
555 return -1;
556 }
557 sa->sa_family = family;
558 s = socket(family, SOCK_STREAM, 0);
559 if (s < 0)
560 return -1;
561 #if defined(BSD4_4) && !defined(__minix)
562 switch (family) {
563 case AF_INET:
564 case AF_INET6:
565 *portp = 0;
566 if (bindresvport(s, (struct sockaddr_in *)(void *)sa) < 0) {
567 int sverr = errno;
568
569 (void)close(s);
570 errno = sverr;
571 return -1;
572 }
573 *alport = (int)ntohs(*portp);
574 return s;
575 default:
576 /* is it necessary to try keep code for other AFs? */
577 break;
578 }
579 #endif
580 for (;;) {
581 *portp = htons((u_short)*alport);
582 if (bind(s, sa, salen) >= 0)
583 return s;
584 if (errno != EADDRINUSE) {
585 (void)close(s);
586 return -1;
587 }
588 (*alport)--;
589 if (*alport == IPPORT_RESERVED/2) {
590 (void)close(s);
591 errno = EAGAIN; /* close */
592 return -1;
593 }
594 }
595 }
596
597 int __check_rhosts_file = 1;
598 const char *__rcmd_errstr;
599
600 int
ruserok(const char * rhost,int superuser,const char * ruser,const char * luser)601 ruserok(const char *rhost, int superuser, const char *ruser, const char *luser)
602 {
603 struct addrinfo hints, *res, *r;
604 int error;
605
606 _DIAGASSERT(rhost != NULL);
607 _DIAGASSERT(ruser != NULL);
608 _DIAGASSERT(luser != NULL);
609
610 memset(&hints, 0, sizeof(hints));
611 hints.ai_family = PF_UNSPEC;
612 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
613 error = getaddrinfo(rhost, "0", &hints, &res);
614 if (error)
615 return -1;
616
617 for (r = res; r; r = r->ai_next) {
618 if (iruserok_sa(r->ai_addr, (int)r->ai_addrlen, superuser,
619 ruser, luser) == 0) {
620 freeaddrinfo(res);
621 return 0;
622 }
623 }
624 freeaddrinfo(res);
625 return -1;
626 }
627
628 /*
629 * New .rhosts strategy: We are passed an ip address. We spin through
630 * hosts.equiv and .rhosts looking for a match. When the .rhosts only
631 * has ip addresses, we don't have to trust a nameserver. When it
632 * contains hostnames, we spin through the list of addresses the nameserver
633 * gives us and look for a match.
634 *
635 * Returns 0 if ok, -1 if not ok.
636 */
637 int
iruserok(u_int32_t raddr,int superuser,const char * ruser,const char * luser)638 iruserok(u_int32_t raddr, int superuser, const char *ruser, const char *luser)
639 {
640 struct sockaddr_in irsin;
641
642 memset(&irsin, 0, sizeof(irsin));
643 irsin.sin_family = AF_INET;
644 #ifdef BSD4_4
645 irsin.sin_len = sizeof(irsin);
646 #endif
647 memcpy(&irsin.sin_addr, &raddr, sizeof(irsin.sin_addr));
648 return iruserok_sa(&irsin, (socklen_t)sizeof(irsin), superuser, ruser,
649 luser);
650 }
651
652 /*
653 * 2nd and 3rd arguments are typed like this, to avoid dependency between
654 * unistd.h and sys/socket.h. There's no better way.
655 */
656 int
iruserok_sa(const void * raddr,int rlen,int superuser,const char * ruser,const char * luser)657 iruserok_sa(const void *raddr, int rlen, int superuser, const char *ruser,
658 const char *luser)
659 {
660 const struct sockaddr *sa;
661 struct stat sbuf;
662 struct passwd *pwd, pwres;
663 FILE *hostf;
664 uid_t uid;
665 gid_t gid;
666 int isvaliduser;
667 char pbuf[MAXPATHLEN];
668 char pwbuf[1024];
669
670 _DIAGASSERT(raddr != NULL);
671 _DIAGASSERT(ruser != NULL);
672 _DIAGASSERT(luser != NULL);
673
674 sa = raddr;
675
676 __rcmd_errstr = NULL;
677
678 hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "re");
679
680 if (hostf) {
681 if (__ivaliduser_sa(hostf, sa, (socklen_t)rlen, luser,
682 ruser) == 0) {
683 (void)fclose(hostf);
684 return 0;
685 }
686 (void)fclose(hostf);
687 }
688
689 isvaliduser = -1;
690 if (__check_rhosts_file || superuser) {
691
692 if (getpwnam_r(luser, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0
693 || pwd == NULL)
694 return -1;
695 (void)strlcpy(pbuf, pwd->pw_dir, sizeof(pbuf));
696 (void)strlcat(pbuf, "/.rhosts", sizeof(pbuf));
697
698 /*
699 * Change effective uid while opening and reading .rhosts.
700 * If root and reading an NFS mounted file system, can't
701 * read files that are protected read/write owner only.
702 */
703 uid = geteuid();
704 gid = getegid();
705 (void)setegid(pwd->pw_gid);
706 (void)initgroups(pwd->pw_name, pwd->pw_gid);
707 (void)seteuid(pwd->pw_uid);
708 hostf = fopen(pbuf, "re");
709
710 if (hostf != NULL) {
711 /*
712 * If not a regular file, or is owned by someone other
713 * than user or root or if writable by anyone but the
714 * owner, quit.
715 */
716 if (lstat(pbuf, &sbuf) < 0)
717 __rcmd_errstr = ".rhosts lstat failed";
718 else if (!S_ISREG(sbuf.st_mode))
719 __rcmd_errstr = ".rhosts not regular file";
720 else if (fstat(fileno(hostf), &sbuf) < 0)
721 __rcmd_errstr = ".rhosts fstat failed";
722 else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
723 __rcmd_errstr = "bad .rhosts owner";
724 else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
725 __rcmd_errstr =
726 ".rhosts writable by other than owner";
727 else
728 isvaliduser =
729 __ivaliduser_sa(hostf, sa, (socklen_t)rlen,
730 luser, ruser);
731
732 (void)fclose(hostf);
733 }
734 (void)seteuid(uid);
735 (void)setegid(gid);
736
737 }
738 return isvaliduser;
739 }
740
741 /*
742 * XXX
743 * Don't make static, used by lpd(8). We will be able to change the function
744 * into static function, when we bump libc major #.
745 *
746 * Returns 0 if ok, -1 if not ok.
747 */
748 #ifdef notdef /*_LIBC*/
749 static
750 #endif
751 int
__ivaliduser(FILE * hostf,u_int32_t raddr,const char * luser,const char * ruser)752 __ivaliduser(FILE *hostf, u_int32_t raddr, const char *luser,
753 const char *ruser)
754 {
755 struct sockaddr_in ivusin;
756
757 memset(&ivusin, 0, sizeof(ivusin));
758 ivusin.sin_family = AF_INET;
759 #ifdef BSD4_4
760 ivusin.sin_len = sizeof(ivusin);
761 #endif
762 memcpy(&ivusin.sin_addr, &raddr, sizeof(ivusin.sin_addr));
763 return __ivaliduser_sa(hostf, (struct sockaddr *)(void *)&ivusin,
764 (socklen_t)sizeof(ivusin), luser, ruser);
765 }
766
767 #ifdef notdef /*_LIBC*/
768 static
769 #endif
770 int
__ivaliduser_sa(FILE * hostf,const struct sockaddr * raddr,socklen_t salen,const char * luser,const char * ruser)771 __ivaliduser_sa(FILE *hostf, const struct sockaddr *raddr, socklen_t salen,
772 const char *luser, const char *ruser)
773 {
774 char *user, *p;
775 int ch;
776 char buf[MAXHOSTNAMELEN + 128]; /* host + login */
777 const char *auser, *ahost;
778 int hostok, userok;
779 char *rhost = NULL;
780 int firsttime = 1;
781 char domain[MAXHOSTNAMELEN];
782
783 getdomainname(domain, sizeof(domain));
784
785 _DIAGASSERT(hostf != NULL);
786 _DIAGASSERT(luser != NULL);
787 _DIAGASSERT(ruser != NULL);
788
789 while (fgets(buf, (int)sizeof(buf), hostf)) {
790 p = buf;
791 /* Skip lines that are too long. */
792 if (strchr(p, '\n') == NULL) {
793 while ((ch = getc(hostf)) != '\n' && ch != EOF)
794 ;
795 continue;
796 }
797 while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
798 *p = isupper((unsigned char)*p) ?
799 tolower((unsigned char)*p) : *p;
800 p++;
801 }
802 if (*p == ' ' || *p == '\t') {
803 *p++ = '\0';
804 while (*p == ' ' || *p == '\t')
805 p++;
806 user = p;
807 while (*p != '\n' && *p != ' ' &&
808 *p != '\t' && *p != '\0')
809 p++;
810 } else
811 user = p;
812 *p = '\0';
813
814 if (p == buf)
815 continue;
816
817 auser = *user ? user : luser;
818 ahost = buf;
819
820 if (ahost[0] == '+')
821 switch (ahost[1]) {
822 case '\0':
823 hostok = 1;
824 break;
825
826 case '@':
827 if (firsttime) {
828 rhost = __gethostloop(raddr, salen);
829 firsttime = 0;
830 }
831 if (rhost)
832 hostok = innetgr(&ahost[2], rhost,
833 NULL, domain);
834 else
835 hostok = 0;
836 break;
837
838 default:
839 hostok = __icheckhost(raddr, salen, &ahost[1]);
840 break;
841 }
842 else if (ahost[0] == '-')
843 switch (ahost[1]) {
844 case '\0':
845 hostok = -1;
846 break;
847
848 case '@':
849 if (firsttime) {
850 rhost = __gethostloop(raddr, salen);
851 firsttime = 0;
852 }
853 if (rhost)
854 hostok = -innetgr(&ahost[2], rhost,
855 NULL, domain);
856 else
857 hostok = 0;
858 break;
859
860 default:
861 hostok =
862 -__icheckhost(raddr, salen, &ahost[1]);
863 break;
864 }
865 else
866 hostok = __icheckhost(raddr, salen, ahost);
867
868
869 if (auser[0] == '+')
870 switch (auser[1]) {
871 case '\0':
872 userok = 1;
873 break;
874
875 case '@':
876 userok = innetgr(&auser[2], NULL, ruser,
877 domain);
878 break;
879
880 default:
881 userok = strcmp(ruser, &auser[1]) == 0;
882 break;
883 }
884 else if (auser[0] == '-')
885 switch (auser[1]) {
886 case '\0':
887 userok = -1;
888 break;
889
890 case '@':
891 userok = -innetgr(&auser[2], NULL, ruser,
892 domain);
893 break;
894
895 default:
896 userok =
897 -(strcmp(ruser, &auser[1]) == 0 ? 1 : 0);
898 break;
899 }
900 else
901 userok = strcmp(ruser, auser) == 0;
902
903 /* Check if one component did not match */
904 if (hostok == 0 || userok == 0)
905 continue;
906
907 /* Check if we got a forbidden pair */
908 if (userok == -1 || hostok == -1)
909 return -1;
910
911 /* Check if we got a valid pair */
912 if (hostok == 1 && userok == 1)
913 return 0;
914 }
915 return -1;
916 }
917
918 /*
919 * Returns "true" if match, 0 if no match.
920 */
921 static int
__icheckhost(const struct sockaddr * raddr,socklen_t salen,const char * lhost)922 __icheckhost(const struct sockaddr *raddr, socklen_t salen, const char *lhost)
923 {
924 struct addrinfo hints, *res, *r;
925 char h1[NI_MAXHOST], h2[NI_MAXHOST];
926 int error;
927 const int niflags = NI_NUMERICHOST;
928
929 _DIAGASSERT(raddr != NULL);
930 _DIAGASSERT(lhost != NULL);
931
932 h1[0] = '\0';
933 if (getnameinfo(raddr, salen, h1, (socklen_t)sizeof(h1), NULL, 0,
934 niflags) != 0)
935 return 0;
936
937 /* Resolve laddr into sockaddr */
938 memset(&hints, 0, sizeof(hints));
939 hints.ai_family = raddr->sa_family;
940 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
941 res = NULL;
942 error = getaddrinfo(lhost, "0", &hints, &res);
943 if (error)
944 return 0;
945
946 /*
947 * Try string comparisons between raddr and laddr.
948 */
949 for (r = res; r; r = r->ai_next) {
950 h2[0] = '\0';
951 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2,
952 (socklen_t)sizeof(h2), NULL, 0, niflags) != 0)
953 continue;
954 if (strcmp(h1, h2) == 0) {
955 freeaddrinfo(res);
956 return 1;
957 }
958 }
959
960 /* No match. */
961 freeaddrinfo(res);
962 return 0;
963 }
964
965 /*
966 * Return the hostname associated with the supplied address.
967 * Do a reverse lookup as well for security. If a loop cannot
968 * be found, pack the numeric IP address into the string.
969 */
970 static char *
__gethostloop(const struct sockaddr * raddr,socklen_t salen)971 __gethostloop(const struct sockaddr *raddr, socklen_t salen)
972 {
973 static char remotehost[NI_MAXHOST];
974 char h1[NI_MAXHOST], h2[NI_MAXHOST];
975 struct addrinfo hints, *res, *r;
976 int error;
977 const int niflags = NI_NUMERICHOST;
978
979 _DIAGASSERT(raddr != NULL);
980
981 h1[0] = remotehost[0] = '\0';
982 if (getnameinfo(raddr, salen, remotehost, (socklen_t)sizeof(remotehost),
983 NULL, 0, NI_NAMEREQD) != 0)
984 return NULL;
985 if (getnameinfo(raddr, salen, h1, (socklen_t)sizeof(h1), NULL, 0,
986 niflags) != 0)
987 return NULL;
988
989 /*
990 * Look up the name and check that the supplied
991 * address is in the list
992 */
993 memset(&hints, 0, sizeof(hints));
994 hints.ai_family = raddr->sa_family;
995 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
996 hints.ai_flags = AI_CANONNAME;
997 res = NULL;
998 error = getaddrinfo(remotehost, "0", &hints, &res);
999 if (error)
1000 return NULL;
1001
1002 for (r = res; r; r = r->ai_next) {
1003 h2[0] = '\0';
1004 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2,
1005 (socklen_t)sizeof(h2), NULL, 0, niflags) != 0)
1006 continue;
1007 if (strcmp(h1, h2) == 0) {
1008 freeaddrinfo(res);
1009 return remotehost;
1010 }
1011 }
1012
1013 /*
1014 * either the DNS adminstrator has made a configuration
1015 * mistake, or someone has attempted to spoof us
1016 */
1017 syslog(LOG_NOTICE, "rcmd: address %s not listed for host %s",
1018 h1, res->ai_canonname ? res->ai_canonname : remotehost);
1019 freeaddrinfo(res);
1020 return NULL;
1021 }
1022