home *** CD-ROM | disk | FTP | other *** search
- /* Public domain. */
-
- #include <stdio.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/stat.h>
- #include <sys/wait.h>
- #include <sys/un.h>
- #include <sys/time.h>
- #include "dupdup.h"
- #include "malloc.h"
- #include "errno.h"
- #include "username.h"
- #include "env.h"
- #include "getopt.h"
- #include "numfiles.h"
-
- static int flagprintlocal = 0;
- static int flagverbose = 1;
- static struct sockaddr_un sabind;
- static int flagbindok = 0;
-
- SIGRET sigterm()
- {
- if (flagbindok)
- unlink(sabind.sun_path);
- exit(0);
- }
-
- SIGRET sigchld()
- {
- wait((union wait *) 0); /*XXX*/
- }
-
- static char errbuf[500];
- void die(n,s) int n; char *s;
- { if (flagverbose) fprintf(stderr,"undomserver: fatal: %s\n",s); exit(n); }
- void dies(n,s,t) int n; char *s; char *t;
- { if (flagverbose) fprintf(stderr,"undomserver: fatal: %s%s\n",s,t); exit(n); }
- void diep(n,s) int n; char *s;
- { if (flagverbose) sprintf(errbuf,"undomserver: fatal: %s",s); perror(errbuf); exit(n); }
- void warnp(s) char *s;
- { if (flagverbose) sprintf(errbuf,"undomserver: warning: %s",s); perror(errbuf); }
- void warnsp(s,t) char *s; char *t;
- { if (flagverbose) sprintf(errbuf,"undomserver: warning: %s%s",s,t); perror(errbuf); }
-
- main(argc,argv,envp)
- int argc;
- char *argv[];
- char *envp[];
- {
- int s;
- int dummy;
- int t;
- int *d;
- int tdup;
- int numfiles;
- static struct sockaddr_un sa;
- int uid;
- int arguid;
- int i;
- char undomlocal[200]; /*XXX*/
- char undomremote[300]; /*XXX*/
- unsigned char urlen;
- int opt;
-
- while ((opt = getopt(argc,argv,"qQ4")) != EOF)
- switch(opt)
- {
- case 'q':
- flagverbose = 0;
- break;
- case 'Q':
- flagverbose = 1;
- break;
- case '4':
- flagprintlocal = 1;
- break;
- case '?':
- break; /*XXX*/
- default:
- break; /*XXX*/
- }
- argc -= optind; argv += optind;
-
- if (argc < 3)
- die(1,"need at least three arguments");
-
- uid = getuid();
- if (username2uid(argv[0],&arguid) == -1)
- dies(2,"cannot figure out username ",argv[0]);
- if (uid && (uid != arguid))
- dies(3,"permission denied for username ",argv[0]);
-
- for (i = 0;argv[0][i];++i)
- if ((argv[0][i] == '/') || (argv[0][i] == ':') || (argv[0][i] == '.') || (argv[0][i] == '\n'))
- dies(4,"illegal characters in username ",argv[0]);
-
- for (i = 0;argv[1][i];++i)
- if ((argv[1][i] == '/') || (argv[1][i] == '.') || (argv[0][i] == '\n'))
- dies(5,"illegal characters in service ",argv[1]);
-
- close(6);
- close(7); /* XXX: these have implications that should be documented */
-
- numfiles = NUMFILES;
- d = (int *) malloc(numfiles * sizeof(int));
- if (!d)
- die(6,"cannot malloc");
- for (i = 0;i < numfiles;++i)
- d[i] = -1;
-
- if (strlen(UNDOMDIR) + strlen(argv[0]) + strlen(argv[1]) + 1 > 100) /*XXX*/
- die(8,"server socket name too long");
- sprintf(sabind.sun_path,"%s/%s:%s",UNDOMDIR,argv[0],argv[1]);
-
- signal(SIGTERM,sigterm);
- signal(SIGINT,sigterm);
- signal(SIGCHLD,sigchld);
-
- if (env_init() == -1)
- die(11,"cannot init environment");
- if (env_put("PROTO=UNDOM") == -1)
- die(11,"out of memory putting PROTO into environment");
- sprintf(undomlocal,"UNDOMLOCAL=%s:%s",argv[0],argv[1]);
- if (env_put(undomlocal) == -1)
- die(11,"out of memory putting UNDOMLOCAL into environment");
- sprintf(undomremote,"UNDOMREMOTE=");
-
- if (flagprintlocal)
- {
- FILE *fd4;
- fd4 = fdopen(4,"w");
- if (!fd4)
- die(1,"cannot print UNDOMLOCAL on fd 4");
- fprintf(fd4,"%s:%s\n",argv[0],argv[1]);
- fclose(fd4);
- close(4); /* just in case */
- }
-
- s = socket(AF_UNIX,SOCK_STREAM,0);
- if (s == -1)
- diep(7,"cannot create server socket");
- sabind.sun_family = AF_UNIX;
- if (bind(s,(struct sockaddr *) &sabind,strlen(sabind.sun_path) + 2) == -1)
- dies(9,"cannot bind server socket ",sabind.sun_path);
- /* XXX: there's a race right here */
- flagbindok = 1;
- if (listen(s,5) == -1)
- {
- unlink(sabind.sun_path);
- diep(10,"cannot listen on server socket");
- }
-
- for (;;)
- {
- /* XXX: don't accept too many requests at once? */
- dummy = sizeof(sa);
- t = accept(s,(struct sockaddr *) &sa,&dummy);
- if (t == -1) /* so when can this happen? */
- {
- switch(errno)
- {
- case SIGCHLD: break; /*XXX*/
- case EINTR: break; /*XXX*/
- case EMFILE: sleep(1); break; /*XXX*/
- case ENFILE: sleep(1); break; /*XXX*/
- default: ; /*XXX*/
- }
- continue;
- }
-
- if (((i = read(t,&urlen,1)) == -1) || (!i))
- continue; /* XXX: error message? */
- if (urlen == 0) /* XXX: undomkiller */
- sigterm();
-
- switch(fork())
- {
- case -1:
- warnp("cannot fork");
- break;
-
- case 0:
-
- if (urlen > sizeof(undomremote) - strlen(undomremote) - 5)
- die(1,"remote username too long");
- if (read(t,undomremote + strlen(undomremote),urlen) < urlen)
- die(1,"cannot read remote username");
- undomremote[sizeof(undomremote) - 1] = 0; /* just in case */
- /* XXX: warn if it's not already just the right length? */
-
- if (env_put(undomremote) == -1)
- die(1,"out of memory putting UNDOMREMOTE into environment");
-
- tdup = dup(t);
- if (tdup == -1)
- diep(1,"cannot dup connection to client");
-
- /* Now put t into descriptor 6 and tdup into descriptor 7. */
- /* XXX: should check that t and tdup are small enough */
- d[t] = 6;
- d[tdup] = 7;
- if (dupdup(d,numfiles) == -1)
- diep(1,"cannot dup connection to client");
-
- if (setreuid(uid,uid) == -1)
- diep(1,"cannot setreuid"); /* will never happen */
- execvp(argv[2],argv + 2);
- warnsp("cannot exec ",argv[2]);
- exit(1);
-
- default:
- close(t);
- }
- }
- }
-