]> git.ipfire.org Git - thirdparty/glibc.git/blob - inet/rcmd.c
* sysdeps/posix/system.c (do_system): New function, guts broken out of
[thirdparty/glibc.git] / inet / rcmd.c
1 /*
2 * Copyright (C) 1998 WIDE Project.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the project nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29 /*
30 * Copyright (c) 1983, 1993, 1994
31 * The Regents of the University of California. All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 4. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 */
57
58 #if defined(LIBC_SCCS) && !defined(lint)
59 static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94";
60 #endif /* LIBC_SCCS and not lint */
61
62 #include <sys/param.h>
63 #include <sys/poll.h>
64 #include <sys/socket.h>
65 #include <sys/stat.h>
66
67 #include <netinet/in.h>
68 #include <arpa/inet.h>
69
70 #include <alloca.h>
71 #include <signal.h>
72 #include <fcntl.h>
73 #include <netdb.h>
74 #include <unistd.h>
75 #include <pwd.h>
76 #include <errno.h>
77 #include <stdio.h>
78 #include <stdio_ext.h>
79 #include <ctype.h>
80 #include <string.h>
81 #include <libintl.h>
82 #include <stdlib.h>
83 #ifdef USE_IN_LIBIO
84 # include <wchar.h>
85 #endif
86
87
88 int __ivaliduser (FILE *, u_int32_t, const char *, const char *);
89 static int __validuser2_sa (FILE *, struct sockaddr *, size_t,
90 const char *, const char *, const char *);
91 static int ruserok2_sa (struct sockaddr *ra, size_t ralen,
92 int superuser, const char *ruser,
93 const char *luser, const char *rhost);
94 static int ruserok_sa (struct sockaddr *ra, size_t ralen,
95 int superuser, const char *ruser,
96 const char *luser);
97 int iruserok_af (const void *raddr, int superuser, const char *ruser,
98 const char *luser, sa_family_t af);
99 int iruserok (u_int32_t raddr, int superuser, const char *ruser,
100 const char *luser);
101
102 static char ahostbuf[NI_MAXHOST];
103
104 int
105 rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af)
106 char **ahost;
107 u_short rport;
108 const char *locuser, *remuser, *cmd;
109 int *fd2p;
110 sa_family_t af;
111 {
112 char paddr[INET6_ADDRSTRLEN];
113 struct addrinfo hints, *res, *ai;
114 struct sockaddr_storage from;
115 struct pollfd pfd[2];
116 int32_t oldmask;
117 pid_t pid;
118 int s, lport, timo, error;
119 char c;
120 int refused;
121 char num[8];
122 ssize_t n;
123
124 if (af != AF_INET && af != AF_INET6 && af != AF_UNSPEC)
125 {
126 __set_errno (EAFNOSUPPORT);
127 return -1;
128 }
129
130 pid = __getpid();
131
132 memset(&hints, '\0', sizeof(hints));
133 hints.ai_flags = AI_CANONNAME;
134 hints.ai_family = af;
135 hints.ai_socktype = SOCK_STREAM;
136 (void)__snprintf(num, sizeof(num), "%d", ntohs(rport));
137 error = getaddrinfo(*ahost, num, &hints, &res);
138 if (error) {
139 #ifdef USE_IN_LIBIO
140 if (_IO_fwide (stderr, 0) > 0)
141 __fwprintf(stderr, L"rcmd: getaddrinfo: %s\n",
142 gai_strerror(error));
143 else
144 #endif
145 fprintf(stderr, "rcmd: getaddrinfo: %s\n",
146 gai_strerror(error));
147 return (-1);
148 }
149
150 pfd[0].events = POLLIN;
151 pfd[1].events = POLLIN;
152
153 if (res->ai_canonname){
154 strncpy(ahostbuf, res->ai_canonname, sizeof(ahostbuf));
155 ahostbuf[sizeof(ahostbuf)-1] = '\0';
156 *ahost = ahostbuf;
157 }
158 else
159 *ahost = NULL;
160 ai = res;
161 refused = 0;
162 oldmask = __sigblock(sigmask(SIGURG));
163 for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
164 char errbuf[200];
165
166 s = rresvport_af(&lport, ai->ai_family);
167 if (s < 0) {
168 if (errno == EAGAIN) {
169 #ifdef USE_IN_LIBIO
170 if (_IO_fwide (stderr, 0) > 0)
171 __fwprintf(stderr, L"%s",
172 _("rcmd: socket: All ports in use\n"));
173 else
174 #endif
175 fputs(_("rcmd: socket: All ports in use\n"),
176 stderr);
177 } else {
178 #ifdef USE_IN_LIBIO
179 if (_IO_fwide (stderr, 0) > 0)
180 __fwprintf(stderr,
181 L"rcmd: socket: %m\n");
182 else
183 #endif
184 fprintf(stderr, "rcmd: socket: %m\n");
185 }
186 __sigsetmask(oldmask);
187 freeaddrinfo(res);
188 return -1;
189 }
190 __fcntl(s, F_SETOWN, pid);
191 if (__connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
192 break;
193 (void)__close(s);
194 if (errno == EADDRINUSE) {
195 lport--;
196 continue;
197 }
198 if (errno == ECONNREFUSED)
199 refused = 1;
200 if (ai->ai_next != NULL) {
201 int oerrno = errno;
202 char *buf = NULL;
203
204 getnameinfo(ai->ai_addr, ai->ai_addrlen,
205 paddr, sizeof(paddr),
206 NULL, 0,
207 NI_NUMERICHOST);
208
209 if (__asprintf (&buf, _("connect to address %s: "),
210 paddr) >= 0)
211 {
212 #ifdef USE_IN_LIBIO
213 if (_IO_fwide (stderr, 0) > 0)
214 __fwprintf(stderr, L"%s", buf);
215 else
216 #endif
217 fputs (buf, stderr);
218 free (buf);
219 }
220 __set_errno (oerrno);
221 perror(0);
222 ai = ai->ai_next;
223 getnameinfo(ai->ai_addr, ai->ai_addrlen,
224 paddr, sizeof(paddr),
225 NULL, 0,
226 NI_NUMERICHOST);
227 if (__asprintf (&buf, _("Trying %s...\n"), paddr) >= 0)
228 {
229 #ifdef USE_IN_LIBIO
230 if (_IO_fwide (stderr, 0) > 0)
231 __fwprintf (stderr, L"%s", buf);
232 else
233 #endif
234 fputs (buf, stderr);
235 free (buf);
236 }
237 continue;
238 }
239 if (refused && timo <= 16) {
240 (void)__sleep(timo);
241 timo *= 2;
242 ai = res;
243 refused = 0;
244 continue;
245 }
246 freeaddrinfo(res);
247 #ifdef USE_IN_LIBIO
248 if (_IO_fwide (stderr, 0) > 0)
249 (void)__fwprintf(stderr, L"%s: %s\n", *ahost,
250 __strerror_r(errno,
251 errbuf, sizeof (errbuf)));
252 else
253 #endif
254 (void)fprintf(stderr, "%s: %s\n", *ahost,
255 __strerror_r(errno,
256 errbuf, sizeof (errbuf)));
257 __sigsetmask(oldmask);
258 return -1;
259 }
260 lport--;
261 if (fd2p == 0) {
262 __write(s, "", 1);
263 lport = 0;
264 } else {
265 char num[8];
266 int s2 = rresvport_af(&lport, ai->ai_family), s3;
267 socklen_t len = ai->ai_addrlen;
268
269 if (s2 < 0)
270 goto bad;
271 __listen(s2, 1);
272 (void)__snprintf(num, sizeof(num), "%d", lport);
273 if (__write(s, num, strlen(num)+1) != (ssize_t)strlen(num)+1) {
274 char *buf = NULL;
275
276 if (__asprintf (&buf, _("\
277 rcmd: write (setting up stderr): %m\n")) >= 0)
278 {
279 #ifdef USE_IN_LIBIO
280 if (_IO_fwide (stderr, 0) > 0)
281 __fwprintf(stderr, L"%s", buf);
282 else
283 #endif
284 fputs (buf, stderr);
285 free (buf);
286 }
287 (void)__close(s2);
288 goto bad;
289 }
290 pfd[0].fd = s;
291 pfd[1].fd = s2;
292 __set_errno (0);
293 if (__poll (pfd, 2, -1) < 1 || (pfd[1].revents & POLLIN) == 0){
294 char *buf = NULL;
295
296 if ((errno != 0
297 && __asprintf(&buf, _("\
298 rcmd: poll (setting up stderr): %m\n")) >= 0)
299 || (errno == 0
300 && __asprintf(&buf, _("\
301 poll: protocol failure in circuit setup\n")) >= 0))
302 {
303 #ifdef USE_IN_LIBIO
304 if (_IO_fwide (stderr, 0) > 0)
305 __fwprintf (stderr, L"%s", buf);
306 else
307 #endif
308 fputs (buf, stderr);
309 free (buf);
310 }
311 (void)__close(s2);
312 goto bad;
313 }
314 s3 = accept(s2, (struct sockaddr *)&from, &len);
315 switch (from.ss_family) {
316 case AF_INET:
317 rport = ntohs(((struct sockaddr_in *)&from)->sin_port);
318 break;
319 case AF_INET6:
320 rport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port);
321 break;
322 default:
323 rport = 0;
324 break;
325 }
326 (void)__close(s2);
327 if (s3 < 0) {
328 #ifdef USE_IN_LIBIO
329 if (_IO_fwide (stderr, 0) > 0)
330 (void)__fwprintf(stderr,
331 L"rcmd: accept: %m\n");
332 else
333 #endif
334 (void)fprintf(stderr,
335 "rcmd: accept: %m\n");
336 lport = 0;
337 goto bad;
338 }
339 *fd2p = s3;
340
341 if (rport >= IPPORT_RESERVED || rport < IPPORT_RESERVED / 2){
342 char *buf = NULL;
343
344 if (__asprintf(&buf, _("\
345 socket: protocol failure in circuit setup\n")) >= 0)
346 {
347 #ifdef USE_IN_LIBIO
348 if (_IO_fwide (stderr, 0) > 0)
349 __fwprintf (stderr, L"%s", buf);
350 else
351 #endif
352 fputs (buf, stderr);
353 free (buf);
354 }
355 goto bad2;
356 }
357 }
358 (void)__write(s, locuser, strlen(locuser)+1);
359 (void)__write(s, remuser, strlen(remuser)+1);
360 (void)__write(s, cmd, strlen(cmd)+1);
361 n = __read(s, &c, 1);
362 if (n != 1) {
363 char *buf = NULL;
364
365 if ((n == 0
366 && __asprintf(&buf, _("rcmd: %s: short read"),
367 *ahost) >= 0)
368 || (n != 0
369 && __asprintf(&buf, "rcmd: %s: %m\n", *ahost) >= 0))
370 {
371 #ifdef USE_IN_LIBIO
372 if (_IO_fwide (stderr, 0) > 0)
373 __fwprintf (stderr, L"%s", buf);
374 else
375 #endif
376 fputs (buf, stderr);
377 free (buf);
378 }
379 goto bad2;
380 }
381 if (c != 0) {
382 while (__read(s, &c, 1) == 1) {
383 (void)__write(STDERR_FILENO, &c, 1);
384 if (c == '\n')
385 break;
386 }
387 goto bad2;
388 }
389 __sigsetmask(oldmask);
390 freeaddrinfo(res);
391 return s;
392 bad2:
393 if (lport)
394 (void)__close(*fd2p);
395 bad:
396 (void)__close(s);
397 __sigsetmask(oldmask);
398 freeaddrinfo(res);
399 return -1;
400 }
401 libc_hidden_def (rcmd_af)
402
403 int
404 rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
405 char **ahost;
406 u_short rport;
407 const char *locuser, *remuser, *cmd;
408 int *fd2p;
409 {
410 return rcmd_af (ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
411 }
412
413 int
414 rresvport_af(alport, family)
415 int *alport;
416 sa_family_t family;
417 {
418 struct sockaddr_storage ss;
419 int s;
420 size_t len;
421 uint16_t *sport;
422
423 switch(family){
424 case AF_INET:
425 len = sizeof(struct sockaddr_in);
426 sport = &((struct sockaddr_in *)&ss)->sin_port;
427 break;
428 case AF_INET6:
429 len = sizeof(struct sockaddr_in6);
430 sport = &((struct sockaddr_in6 *)&ss)->sin6_port;
431 break;
432 default:
433 __set_errno (EAFNOSUPPORT);
434 return -1;
435 }
436 s = __socket(family, SOCK_STREAM, 0);
437 if (s < 0)
438 return -1;
439
440 memset (&ss, '\0', sizeof(ss));
441 #ifdef SALEN
442 ss.__ss_len = len;
443 #endif
444 ss.ss_family = family;
445
446 for (;;) {
447 *sport = htons((uint16_t) *alport);
448 if (__bind(s, (struct sockaddr *)&ss, len) >= 0)
449 return s;
450 if (errno != EADDRINUSE) {
451 (void)__close(s);
452 return -1;
453 }
454 (*alport)--;
455 if (*alport == IPPORT_RESERVED/2)
456 break;
457 }
458 (void)__close(s);
459 __set_errno (EAGAIN);
460 return -1;
461 }
462 libc_hidden_def (rresvport_af)
463
464 int
465 rresvport(alport)
466 int *alport;
467 {
468 return rresvport_af(alport, AF_INET);
469 }
470
471 int __check_rhosts_file = 1;
472 char *__rcmd_errstr;
473
474 int
475 ruserok_af(rhost, superuser, ruser, luser, af)
476 const char *rhost, *ruser, *luser;
477 int superuser;
478 sa_family_t af;
479 {
480 struct addrinfo hints, *res, *res0;
481 int gai;
482 int ret;
483
484 memset (&hints, '\0', sizeof(hints));
485 hints.ai_family = af;
486 gai = getaddrinfo(rhost, NULL, &hints, &res0);
487 if (gai)
488 return -1;
489 ret = -1;
490 for (res=res0; res; res=res->ai_next)
491 if (ruserok2_sa(res->ai_addr, res->ai_addrlen,
492 superuser, ruser, luser, rhost) == 0){
493 ret = 0;
494 break;
495 }
496 freeaddrinfo(res0);
497 return (ret);
498 }
499 libc_hidden_def (ruserok_af)
500
501 int
502 ruserok(rhost, superuser, ruser, luser)
503 const char *rhost, *ruser, *luser;
504 int superuser;
505 {
506 return ruserok_af(rhost, superuser, ruser, luser, AF_INET);
507 }
508
509 /* Extremely paranoid file open function. */
510 static FILE *
511 iruserfopen (const char *file, uid_t okuser)
512 {
513 struct stat64 st;
514 char *cp = NULL;
515 FILE *res = NULL;
516
517 /* If not a regular file, if owned by someone other than user or
518 root, if writeable by anyone but the owner, or if hardlinked
519 anywhere, quit. */
520 cp = NULL;
521 if (__lxstat64 (_STAT_VER, file, &st))
522 cp = _("lstat failed");
523 else if (!S_ISREG (st.st_mode))
524 cp = _("not regular file");
525 else
526 {
527 res = fopen (file, "r");
528 if (!res)
529 cp = _("cannot open");
530 else if (__fxstat64 (_STAT_VER, fileno (res), &st) < 0)
531 cp = _("fstat failed");
532 else if (st.st_uid && st.st_uid != okuser)
533 cp = _("bad owner");
534 else if (st.st_mode & (S_IWGRP|S_IWOTH))
535 cp = _("writeable by other than owner");
536 else if (st.st_nlink > 1)
537 cp = _("hard linked somewhere");
538 }
539
540 /* If there were any problems, quit. */
541 if (cp != NULL)
542 {
543 __rcmd_errstr = cp;
544 if (res)
545 fclose (res);
546 return NULL;
547 }
548
549 /* No threads use this stream. */
550 __fsetlocking (res, FSETLOCKING_BYCALLER);
551
552 return res;
553 }
554
555 /*
556 * New .rhosts strategy: We are passed an ip address. We spin through
557 * hosts.equiv and .rhosts looking for a match. When the .rhosts only
558 * has ip addresses, we don't have to trust a nameserver. When it
559 * contains hostnames, we spin through the list of addresses the nameserver
560 * gives us and look for a match.
561 *
562 * Returns 0 if ok, -1 if not ok.
563 */
564 static int
565 ruserok2_sa (ra, ralen, superuser, ruser, luser, rhost)
566 struct sockaddr *ra;
567 size_t ralen;
568 int superuser;
569 const char *ruser, *luser, *rhost;
570 {
571 FILE *hostf = NULL;
572 int isbad = -1;
573
574 if (!superuser)
575 hostf = iruserfopen (_PATH_HEQUIV, 0);
576
577 if (hostf)
578 {
579 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
580 fclose (hostf);
581
582 if (!isbad)
583 return 0;
584 }
585
586 if (__check_rhosts_file || superuser)
587 {
588 char *pbuf;
589 struct passwd pwdbuf, *pwd;
590 size_t dirlen;
591 size_t buflen = __sysconf (_SC_GETPW_R_SIZE_MAX);
592 char *buffer = __alloca (buflen);
593 uid_t uid;
594
595 if (__getpwnam_r (luser, &pwdbuf, buffer, buflen, &pwd) != 0
596 || pwd == NULL)
597 return -1;
598
599 dirlen = strlen (pwd->pw_dir);
600 pbuf = alloca (dirlen + sizeof "/.rhosts");
601 __mempcpy (__mempcpy (pbuf, pwd->pw_dir, dirlen),
602 "/.rhosts", sizeof "/.rhosts");
603
604 /* Change effective uid while reading .rhosts. If root and
605 reading an NFS mounted file system, can't read files that
606 are protected read/write owner only. */
607 uid = __geteuid ();
608 seteuid (pwd->pw_uid);
609 hostf = iruserfopen (pbuf, pwd->pw_uid);
610
611 if (hostf != NULL)
612 {
613 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
614 fclose (hostf);
615 }
616
617 seteuid (uid);
618 return isbad;
619 }
620 return -1;
621 }
622 /*
623 * ruserok_sa() is now discussed on ipng, so
624 * currently disabled for external use
625 */
626 static int ruserok_sa(ra, ralen, superuser, ruser, luser)
627 struct sockaddr *ra;
628 size_t ralen;
629 int superuser;
630 const char *ruser, *luser;
631 {
632 return ruserok2_sa(ra, ralen, superuser, ruser, luser, "-");
633 }
634
635 /* This is the exported version. */
636 int
637 iruserok_af (raddr, superuser, ruser, luser, af)
638 const void *raddr;
639 int superuser;
640 const char *ruser, *luser;
641 sa_family_t af;
642 {
643 struct sockaddr_storage ra;
644 size_t ralen;
645
646 memset (&ra, '\0', sizeof(ra));
647 switch (af){
648 case AF_INET:
649 ((struct sockaddr_in *)&ra)->sin_family = AF_INET;
650 memcpy (&(((struct sockaddr_in *)&ra)->sin_addr), raddr,
651 sizeof(struct in_addr));
652 ralen = sizeof(struct sockaddr_in);
653 break;
654 case AF_INET6:
655 ((struct sockaddr_in6 *)&ra)->sin6_family = AF_INET6;
656 memcpy (&(((struct sockaddr_in6 *)&ra)->sin6_addr), raddr,
657 sizeof(struct in6_addr));
658 ralen = sizeof(struct sockaddr_in6);
659 break;
660 default:
661 return 0;
662 }
663 return ruserok_sa ((struct sockaddr *)&ra, ralen, superuser, ruser, luser);
664 }
665 int
666 iruserok (raddr, superuser, ruser, luser)
667 u_int32_t raddr;
668 int superuser;
669 const char *ruser, *luser;
670 {
671 return iruserok_af (&raddr, superuser, ruser, luser, AF_INET);
672 }
673
674 /*
675 * XXX
676 * Don't make static, used by lpd(8).
677 *
678 * This function is not used anymore. It is only present because lpd(8)
679 * calls it (!?!). We simply call __invaliduser2() with an illegal rhost
680 * argument. This means that netgroups won't work in .rhost/hosts.equiv
681 * files. If you want lpd to work with netgroups, fix lpd to use ruserok()
682 * or PAM.
683 * Returns 0 if ok, -1 if not ok.
684 */
685 int
686 __ivaliduser(hostf, raddr, luser, ruser)
687 FILE *hostf;
688 u_int32_t raddr;
689 const char *luser, *ruser;
690 {
691 struct sockaddr_in ra;
692 memset(&ra, '\0', sizeof(ra));
693 ra.sin_family = AF_INET;
694 ra.sin_addr.s_addr = raddr;
695 return __validuser2_sa(hostf, (struct sockaddr *)&ra, sizeof(ra),
696 luser, ruser, "-");
697 }
698
699
700 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
701 static int
702 internal_function
703 __checkhost_sa (struct sockaddr *ra, size_t ralen, char *lhost,
704 const char *rhost)
705 {
706 struct addrinfo hints, *res0, *res;
707 char raddr[INET6_ADDRSTRLEN];
708 int match;
709 int negate=1; /* Multiply return with this to get -1 instead of 1 */
710
711 /* Check nis netgroup. */
712 if (strncmp ("+@", lhost, 2) == 0)
713 return innetgr (&lhost[2], rhost, NULL, NULL);
714
715 if (strncmp ("-@", lhost, 2) == 0)
716 return -innetgr (&lhost[2], rhost, NULL, NULL);
717
718 /* -host */
719 if (strncmp ("-", lhost,1) == 0) {
720 negate = -1;
721 lhost++;
722 } else if (strcmp ("+",lhost) == 0) {
723 return 1; /* asking for trouble, but ok.. */
724 }
725
726 /* Try for raw ip address first. */
727 /* XXX */
728 if (getnameinfo(ra, ralen,
729 raddr, sizeof(raddr), NULL, 0,
730 NI_NUMERICHOST) == 0
731 && strcmp(raddr, lhost) == 0)
732 return negate;
733
734 /* Better be a hostname. */
735 match = 0;
736 memset(&hints, '\0', sizeof(hints));
737 hints.ai_family = ra->sa_family;
738 if (getaddrinfo(lhost, NULL, &hints, &res0) == 0){
739 /* Spin through ip addresses. */
740 for (res = res0; res; res = res->ai_next)
741 {
742 if (res->ai_family == ra->sa_family
743 && !memcmp(res->ai_addr, ra, res->ai_addrlen))
744 {
745 match = 1;
746 break;
747 }
748 }
749 freeaddrinfo (res0);
750 }
751 return negate * match;
752 }
753
754 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
755 static int
756 internal_function
757 __icheckuser (const char *luser, const char *ruser)
758 {
759 /*
760 luser is user entry from .rhosts/hosts.equiv file
761 ruser is user id on remote host
762 */
763
764 /* [-+]@netgroup */
765 if (strncmp ("+@", luser, 2) == 0)
766 return innetgr (&luser[2], NULL, ruser, NULL);
767
768 if (strncmp ("-@", luser,2) == 0)
769 return -innetgr (&luser[2], NULL, ruser, NULL);
770
771 /* -user */
772 if (strncmp ("-", luser, 1) == 0)
773 return -(strcmp (&luser[1], ruser) == 0);
774
775 /* + */
776 if (strcmp ("+", luser) == 0)
777 return 1;
778
779 /* simple string match */
780 return strcmp (ruser, luser) == 0;
781 }
782
783 /*
784 * Returns 1 for blank lines (or only comment lines) and 0 otherwise
785 */
786 static int
787 __isempty (char *p)
788 {
789 while (*p && isspace (*p)) {
790 ++p;
791 }
792
793 return (*p == '\0' || *p == '#') ? 1 : 0 ;
794 }
795
796 /*
797 * Returns 0 if positive match, -1 if _not_ ok.
798 */
799 static int
800 __validuser2_sa(hostf, ra, ralen, luser, ruser, rhost)
801 FILE *hostf;
802 struct sockaddr *ra;
803 size_t ralen;
804 const char *luser, *ruser, *rhost;
805 {
806 register const char *user;
807 register char *p;
808 int hcheck, ucheck;
809 char *buf = NULL;
810 size_t bufsize = 0;
811 int retval = -1;
812
813 while (__getline (&buf, &bufsize, hostf) > 0) {
814 buf[bufsize - 1] = '\0'; /* Make sure it's terminated. */
815 p = buf;
816
817 /* Skip empty or comment lines */
818 if (__isempty (p)) {
819 continue;
820 }
821
822 /* Skip lines that are too long. */
823 if (strchr (p, '\n') == NULL) {
824 int ch = getc_unlocked (hostf);
825
826 while (ch != '\n' && ch != EOF)
827 ch = getc_unlocked (hostf);
828 continue;
829 }
830
831 for (;*p && !isspace(*p); ++p) {
832 *p = _tolower (*p);
833 }
834
835 /* Next we want to find the permitted name for the remote user. */
836 if (*p == ' ' || *p == '\t') {
837 /* <nul> terminate hostname and skip spaces */
838 for (*p++='\0'; *p && isspace (*p); ++p);
839
840 user = p; /* this is the user's name */
841 while (*p && !isspace (*p))
842 ++p; /* find end of user's name */
843 } else
844 user = p;
845
846 *p = '\0'; /* <nul> terminate username (+host?) */
847
848 /* buf -> host(?) ; user -> username(?) */
849
850 /* First check host part */
851 hcheck = __checkhost_sa (ra, ralen, buf, rhost);
852
853 if (hcheck < 0)
854 break;
855
856 if (hcheck) {
857 /* Then check user part */
858 if (! (*user))
859 user = luser;
860
861 ucheck = __icheckuser (user, ruser);
862
863 /* Positive 'host user' match? */
864 if (ucheck > 0) {
865 retval = 0;
866 break;
867 }
868
869 /* Negative 'host -user' match? */
870 if (ucheck < 0)
871 break;
872
873 /* Neither, go on looking for match */
874 }
875 }
876
877 if (buf != NULL)
878 free (buf);
879
880 return retval;
881 }