initial-freebsd-port-work-20010414
[openafs.git] / src / rsh / rcmd.c
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17
18 #if defined(LIBC_SCCS) && !defined(lint)
19 static char sccsid[] = "@(#)rcmd.c      5.20 (Berkeley) 1/24/89";
20 #endif /* LIBC_SCCS and not lint */
21
22 #include <afs/param.h>
23 #ifdef aiws             /*AIX*/
24 #include <sys/types.h>
25 #define MAXHOSTNAMELEN  64      /* use BSD's, not sys/param's */
26 #define MAXPATHLEN      1024    /* from BSD */
27 #include <sys/ioctl.h>          /* for SIOCSPGRP */
28 #endif
29
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <pwd.h>
33 #include <sys/param.h>
34 #include <limits.h>
35 #include <sys/file.h>
36 #ifdef  AFS_SUN5_ENV
37 #include <fcntl.h>
38 #endif
39 #include <unistd.h>             /* select() prototype */
40 #include <sys/types.h>          /* fd_set on older platforms */
41 #include <sys/time.h>           /* struct timeval, select() prototype */
42 #ifndef FD_SET
43 # include <sys/select.h>        /* fd_set on newer platforms */
44 #endif
45 #include <sys/signal.h>
46 #include <sys/socket.h>
47 #include <sys/stat.h>
48
49 #include <netinet/in.h>
50
51 #include <netdb.h>
52 #include <errno.h>
53
54 #if defined(AFS_HPUX_ENV)
55 /* HPUX uses a different call to set[res][gu]ids: */
56 #define seteuid(newEuid)        setresuid(-1, (newEuid), -1)
57 #define setegid(newEgid)        setresgid(-1, (newEgid), -1)
58 #endif /* defined(AFS_HPUX_ENV) */
59
60 #ifdef  TCP_DEBUG
61 #include <sys/syslog.h>
62 #       define  DPRINTF(args)   dprintf args
63
64         dprintf (args)
65         char    *args;
66         {
67         char    **argv;
68         char    buf[BUFSIZ];
69         static  char    prefix[] = "rcmd: ";
70
71                 argv = &args;
72                 vsprintf (buf, argv[0], &argv[1]);
73                 syslog (LOG_DEBUG, "%s %s\n", prefix, buf);
74         }
75 #else
76 #       define  DPRINTF(args)
77 #endif
78
79 char    *index();
80
81 #include <syslog.h>
82
83 static _checkhost();
84
85 #ifdef AFS_HPUX102_ENV
86 int rmcd(ahost, rport, locuser, remuser, cmd, fd2p)
87      char **ahost;
88      int rport;
89      const char *locuser, *remuser, *cmd;
90      int *fd2p;
91 #else
92 #ifdef  AFS_AIX32_ENV
93 rcmd(ahost, rport, locuser, remuser, cmd, fd2p, retry)
94     int retry;
95 #else
96 rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
97 #endif
98         char **ahost;
99         u_short rport;
100 #if defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
101         const char *locuser, *remuser, *cmd;
102 #else
103         char *locuser, *remuser, *cmd;
104 #endif
105         int *fd2p;
106 #endif
107 {
108         int s, timo = 1, pid;
109         sigset_t oldset;
110         sigset_t sigBlock;
111         int someSignals[100];
112         struct servent *sp, *getservbyport();
113         struct sockaddr_in sin, from;
114         char c;
115         int lport = IPPORT_RESERVED - 1;
116         struct hostent *hp;
117         fd_set reads;
118
119         bzero((char *)someSignals, sizeof(someSignals));
120         someSignals[0] = 1<<(SIGURG-1);
121         sigBlock = *((sigset_t *)someSignals);
122
123         pid = getpid();
124         hp = gethostbyname(*ahost); /* CAUTION: this uses global static */
125         /* storage.  ANY OTHER CALLS to gethostbyname (including from within
126            syslog, eg) will trash the contents of hp. BE CAREFUL! */
127         if (hp == 0) {
128                 herror(*ahost);
129                 return (-1);
130         }
131         *ahost = hp->h_name;
132         /* was: syslog(LOG_ERR, "rcmd: host=%s, rport=%d, luser=%s,ruser=%s,cmd=%s,fd2p=%s\n", *ahost,rport,locuser,remuser,cmd,fd2p) 
133         but that trashes hp... */
134         sigprocmask(SIG_BLOCK, &sigBlock, &oldset);
135         for (;;) {
136                 s = rresvport(&lport);
137                 if (s < 0) {
138                         if (errno == EAGAIN)
139                                 fprintf(stderr, "socket: All ports in use\n");
140                         else
141                                 perror("rcmd: socket");
142                         sigprocmask(SIG_SETMASK, &oldset, (sigset_t *)0);
143                         return (-1);
144                 }
145 #ifdef  AFS_AIX32_ENV
146 #ifndef aiws
147                 fcntl(s, F_SETOWN, pid);
148 #else
149                 /* since AIX has no F_SETOWN, we just do the ioctl */
150                 (void)ioctl(s, SIOCSPGRP, &pid );
151 #endif
152 #else
153 #if !defined(AFS_AIX_ENV) && !defined(AFS_HPUX_ENV)
154                 fcntl(s, F_SETOWN, pid);
155 #endif /* !defined(AIX) */
156 #endif
157                 sin.sin_family = hp->h_addrtype;
158 #ifdef  AFS_OSF_ENV
159                 bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr, hp->h_length);
160 #else
161                 bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
162 #endif
163                 sin.sin_port = rport;
164                 /* attempt to remote authenticate first... */
165                 sp = getservbyport((int)rport, "tcp");
166                 if(sp) {
167                     int ok = 0;
168
169                     switch(ta_rauth(s, sp->s_name, sin.sin_addr)) {
170                     case 0: 
171 #ifndef AFS_SGI_ENV
172                         fprintf(stderr,"No remote authentication\n");
173 #endif
174                         close(s);
175                         s = rresvport(&lport);
176                         if (s < 0) {
177                             if (errno == EAGAIN)
178                                 fprintf(stderr, "socket: All ports in use\n");
179                             else
180                                 perror("rcmd: socket");
181                             sigprocmask(SIG_SETMASK, &oldset, (sigset_t *)0);
182                             return (-1);
183                           }
184 #if !defined(AFS_AIX_ENV) && !defined(AFS_HPUX_ENV)
185                         fcntl(s, F_SETOWN, pid);
186 #endif /* !defined(AIX) */
187                         break;
188                     case 1:
189                         ok = 1;
190                         break;
191                     case -1:
192                         fprintf(stderr,"Login incorrect.");
193                         exit(1);
194                         break;
195                     case -2:
196                         fprintf(stderr,"internal failure, ta_rauth\n");
197                         exit(errno);
198                         break;
199                       case -3:
200                         fprintf(stderr,"Cannot connect to remote machine\n");
201                         exit(errno);
202                         break;
203                     }
204                     
205                     if(ok) {
206                         break; /* from for loop */
207                       }
208                   }
209                 if (connect(s, (struct sockaddr *) &sin, sizeof (sin)) >= 0)
210                     break;
211                 (void) close(s);
212                 if (errno == EADDRINUSE) {
213                     lport--;
214                     continue;
215                   }
216                 if (errno == ECONNREFUSED && timo <= 16) {
217 #ifdef  AFS_AIX32_ENV
218                         if (!retry) {
219                                 return(-2);
220                         }
221 #endif
222                     sleep(timo);
223                     timo *= 2;
224                     continue;
225                   }
226                 if (hp->h_addr_list[1] != NULL) {
227                     int oerrno = errno;
228                     
229                     fprintf(stderr,
230                             "connect to address %s: ", inet_ntoa(sin.sin_addr));
231                     errno = oerrno;
232                     perror(0);
233                     hp->h_addr_list++;
234                     bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr,
235                           hp->h_length);
236                     fprintf(stderr, "Trying %s...\n",
237                             inet_ntoa(sin.sin_addr));
238                     continue;
239                   }
240                 perror(hp->h_name);
241                 sigprocmask(SIG_SETMASK, &oldset, (sigset_t *)0);
242                 return (-1);
243         }
244         lport--;
245         if (fd2p == 0) {
246                 write(s, "", 1);
247                 lport = 0;
248         } else {
249                 char num[8];
250                 int s2 = rresvport(&lport), s3;
251                 int len = sizeof (from);
252                 int maxfd = -1;
253
254                 if (s2 < 0)
255                         goto bad;
256                 listen(s2, 1);
257                 (void) sprintf(num, "%d", lport);
258                 if (write(s, num, strlen(num)+1) != strlen(num)+1) {
259                         perror("write: setting up stderr");
260                         (void) close(s2);
261                         goto bad;
262                 }
263                 FD_ZERO(&reads);
264                 FD_SET(s, &reads); if (maxfd < s) maxfd = s;
265                 FD_SET(s2, &reads); if (maxfd < s2) maxfd = s2;
266                 errno = 0;
267                 if (select(maxfd+1, &reads, 0, 0, 0) < 1 ||
268                     !FD_ISSET(s2, &reads)) {
269                         if (errno != 0)
270                                 perror("select: setting up stderr");
271                         else fprintf(stderr,
272                                "select: protocol failure in circuit setup.\n");
273                         (void) close(s2);
274                         goto bad;
275                       }
276                 s3 = accept(s2, (struct sockaddr *) &from, &len);
277                 (void) close(s2);
278                 if (s3 < 0) {
279                         perror("accept");
280                         lport = 0;
281                         goto bad;
282                 }
283                 *fd2p = s3;
284                 from.sin_port = ntohs((u_short)from.sin_port);
285                 if (from.sin_family != AF_INET ||
286                     from.sin_port >= IPPORT_RESERVED ||
287                     from.sin_port < IPPORT_RESERVED / 2) {
288                         fprintf(stderr,
289                             "socket: protocol failure in circuit setup.\n");
290                         goto bad2;
291                 }
292         }
293         (void) write(s, locuser, strlen(locuser)+1);
294         (void) write(s, remuser, strlen(remuser)+1);
295         (void) write(s, cmd, strlen(cmd)+1);
296         errno = 0;
297         if (read(s, &c, 1) < 0) {
298                 perror(*ahost);
299                 goto bad2;
300         }
301         if (c != 0) {
302 #ifdef  AFS_OSF_ENV
303                 /*
304                  *   Two different protocols seem to be used;
305                  *   one prepends a "message" byte with a "small"
306                  *   number; the other one just sends the message
307                  */
308                 if (isalnum(c))
309                         (void) write(2, &c, 1);
310
311 #endif
312                 while (read(s, &c, 1) == 1) {
313                         (void) write(2, &c, 1);
314                         if (c == '\n')
315                                 break;
316                 }
317                 goto bad2;
318         }
319         sigprocmask(SIG_SETMASK, &oldset, (sigset_t *)0);
320         return (s);
321 bad2:
322         if (lport)
323                 (void) close(*fd2p);
324 bad:
325         (void) close(s);
326         sigprocmask(SIG_SETMASK, &oldset, (sigset_t *)0);
327         return (-1);
328 }
329
330 #ifndef AFS_AIX32_ENV
331 rresvport(alport)
332         int *alport;
333 {
334         struct sockaddr_in sin;
335         int s;
336
337         sin.sin_family = AF_INET;
338         sin.sin_addr.s_addr = INADDR_ANY;
339         s = socket(AF_INET, SOCK_STREAM, 0);
340         if (s < 0)
341                 return (-1);
342         for (;;) {
343                 sin.sin_port = htons((u_short)*alport);
344                 if (bind(s, (struct sockaddr *) &sin, sizeof (sin)) >= 0)
345                         return (s);
346                 if (errno != EADDRINUSE) {
347                         (void) close(s);
348                         return (-1);
349                 }
350                 (*alport)--;
351                 if (*alport == IPPORT_RESERVED/2) {
352                         (void) close(s);
353                         errno = EAGAIN;         /* close */
354                         return (-1);
355                 }
356         }
357 }
358 #endif
359
360 int     _check_rhosts_file = 1;
361
362 #if defined(AFS_HPUX102_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
363 ruserok(rhost, superuser, ruser, luser)
364         const char *rhost;
365         int superuser;
366         const char *ruser, *luser;
367 #else
368 ruserok(rhost, superuser, ruser, luser)
369         int superuser;
370         char *rhost;
371         char *ruser, *luser;
372 #endif
373 {
374         FILE *hostf;
375         char fhost[MAXHOSTNAMELEN];
376         int first = 1;
377         register char *sp, *p;
378         int baselen = -1;
379         int suid, sgid;
380         int group_list_size= -1;
381         gid_t groups[NGROUPS_MAX];
382         sp = rhost;
383         p = fhost;
384         while (*sp) {
385                 if (*sp == '.') {
386                         if (baselen == -1)
387                                 baselen = sp - rhost;
388                         *p++ = *sp++;
389                 } else {
390                         *p++ = isupper(*sp) ? tolower(*sp++) : *sp++;
391                 }
392         }
393         *p = '\0';
394         hostf = superuser ? (FILE *)0 : fopen("/etc/hosts.equiv", "r");
395 again:
396         if (hostf) {
397                 if (!_validuser(hostf, fhost, luser, ruser, baselen)) {
398                         (void) fclose(hostf);
399 #ifdef  AFS_OSF_ENV
400                         if (first == 0) {
401                                 (void) seteuid(suid);
402                                 (void) setegid(sgid);
403                                 if(group_list_size >= 0)
404                                         (void) setgroups(group_list_size, groups);
405                         }
406 #endif
407                         return(0);
408                 }
409                 (void) fclose(hostf);
410         }
411         if (first == 1 && (_check_rhosts_file || superuser)) {
412                 struct stat sbuf;
413                 struct passwd *pwd, *getpwnam();
414                 char pbuf[MAXPATHLEN];
415
416                 first = 0;
417                 suid = geteuid();
418                 sgid = getegid();
419                 group_list_size = getgroups(NGROUPS_MAX, groups);
420                 if ((pwd = getpwnam(luser)) == NULL)
421                         return(-1);
422                 if(setegid(pwd->pw_gid) >= 0)
423                         (void) initgroups(luser, pwd->pw_gid);
424                 (void) seteuid(pwd->pw_uid);
425                 (void)strcpy(pbuf, pwd->pw_dir);
426                 (void)strcat(pbuf, "/.rhosts");
427                 if ((hostf = fopen(pbuf, "r")) == NULL)
428                     goto bad;
429                 /*
430                  * if owned by someone other than user or root or if
431                  * writeable by anyone but the owner, quit
432                  */
433                 if (fstat(fileno(hostf), &sbuf) ||
434                     sbuf.st_uid && sbuf.st_uid != pwd->pw_uid ||
435                     sbuf.st_mode&022) {
436                         fclose(hostf);
437                         goto bad;
438                 }
439                 goto again;
440         }
441 bad:
442         if (first == 0) {
443                 (void) seteuid(suid);
444                 (void) setegid(sgid);
445                 if(group_list_size >= 0)
446                         (void) setgroups(group_list_size, groups);
447         }
448         return (-1);
449 }
450
451 /* don't make static, used by lpd(8) */
452 _validuser(hostf, rhost, luser, ruser, baselen)
453         char *rhost, *luser, *ruser;
454         FILE *hostf;
455         int baselen;
456 {
457         char *user;
458         char ahost[MAXHOSTNAMELEN * 4];
459         register char *p;
460 #ifdef  AFS_AIX32_ENV
461 #include <arpa/nameser.h>
462         int hostmatch, usermatch;
463         char domain[MAXDNAME], *dp;
464
465         dp = NULL;
466         if (getdomainname(domain, sizeof(domain)) == 0)
467             dp = domain;
468 #endif
469         while (fgets(ahost, sizeof (ahost), hostf)) {
470 #ifdef  AFS_AIX32_ENV
471                 hostmatch = usermatch = 0;
472                 p = ahost;
473                 if (*p == '#' || *p == '\n')  /* ignore comments and blanks */
474                         continue;
475                 while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0')
476                         p++;
477                 if (*p == ' ' || *p == '\t') {
478                         *p++ = '\0';
479                         while (*p == ' ' || *p == '\t')
480                                 p++;
481                         user = p;
482                         while (*p != '\n' && *p != ' ' && *p != '\t' &&
483                             *p != '\0')
484                                 p++;
485                 } else
486                         user = p;
487                 *p = '\0';
488                 /*
489                  *  + - anything goes
490                  *  +@<name> - group <name> allowed
491                  *  -@<name> - group <name> disallowed
492                  *  -<name> - host <name> disallowed
493                  */
494                 if (ahost[0] == '+' && ahost[1] == 0)
495                     hostmatch = 1;
496                 else if (ahost[0] == '+' && ahost[1] == '@')
497                     hostmatch = innetgr(ahost + 2, rhost, NULL, dp);
498                 else if (ahost[0] == '-' && ahost[1] == '@') {
499                     if (innetgr(ahost + 2, rhost, NULL, dp))
500                         return(-1);
501                 } else if (ahost[0] == '-') {
502                     if (_checkhost(rhost, ahost+1, baselen))
503                         return(-1);
504                 } else
505                     hostmatch = _checkhost(rhost, ahost, baselen);
506                 if (user[0]) {
507                     if (user[0] == '+' && user[1] == 0)
508                         usermatch = 1;
509                     else if (user[0] == '+' && user[1] == '@')
510                         usermatch = innetgr(user+2, NULL, ruser, dp);
511                     else if (user[0] == '-' && user[1] == '@') {
512                         if (hostmatch && innetgr(user+2, NULL, ruser, dp))
513                             return(-1);
514                     } else if (user[0] == '-') {
515                         if (hostmatch && !strcmp(user+1, ruser))
516                             return(-1);
517                     } else
518                         usermatch = !strcmp(user, ruser);
519                 } else
520                     usermatch = !strcmp(ruser, luser);
521                 if (hostmatch && usermatch)
522                     return(0);
523 #else
524                 p = ahost;
525                 while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
526                         *p = isupper(*p) ? tolower(*p) : *p;
527                         p++;
528                 }
529                 if (*p == ' ' || *p == '\t') {
530                         *p++ = '\0';
531                         while (*p == ' ' || *p == '\t')
532                                 p++;
533                         user = p;
534                         while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0')
535                                 p++;
536                 } else
537                         user = p;
538                 *p = '\0';
539                 if (_checkhost(rhost, ahost, baselen) &&
540                     !strcmp(ruser, *user ? user : luser)) {
541                         return (0);
542                 }
543 #endif
544         }
545         return (-1);
546 }
547
548 static
549 _checkhost(rhost, lhost, len)
550         char *rhost, *lhost;
551         int len;
552 {
553         static char ldomain[MAXHOSTNAMELEN + 1];
554         static char *domainp = NULL;
555         static int nodomain = 0;
556         register char *cp;
557
558 #ifdef  AFS_AIX32_ENV
559         struct hostent *hp;
560         long addr;
561
562         /*
563          * check for ip address and do a lookup to convert to hostname
564          */
565         if (isinet_addr(lhost) && (addr = inet_addr(lhost)) != -1 &&
566             (hp = gethostbyaddr(&addr, sizeof(addr), AF_INET)))
567                 lhost = hp->h_name;
568
569 #endif
570         if (len == -1) {
571 #ifdef  AFS_AIX32_ENV
572             /* see if hostname from file has a domain name */
573             for (cp = lhost; *cp; ++cp) {
574                 if (*cp == '.') {
575                     len = cp - lhost;
576                     break;
577                 }
578             }
579 #endif
580             return(!strcmp(rhost, lhost));
581         }
582         if (strncmp(rhost, lhost, len))
583                 return(0);
584         if (!strcmp(rhost, lhost))
585                 return(1);
586 #ifdef  AFS_AIX32_ENV
587         if (*(lhost + len) != '\0' && *(rhost + len) != '\0')
588 #else
589         if (*(lhost + len) != '\0')
590 #endif
591                 return(0);
592         if (nodomain)
593                 return(0);
594         if (!domainp) {
595                 if (gethostname(ldomain, sizeof(ldomain)) == -1) {
596                         nodomain = 1;
597                         return(0);
598                 }
599                 ldomain[MAXHOSTNAMELEN] = '\0';
600                 if ((domainp = index(ldomain, '.')) == (char *)NULL) {
601                         nodomain = 1;
602                         return(0);
603                 }
604                 for (cp = ++domainp; *cp; ++cp)
605                         if (isupper(*cp))
606                                 *cp = tolower(*cp);
607         }
608         return(!strcmp(domainp, rhost + len +1));
609 }