-/*
- * Transmit a packet. Add security information first.
- */
-static int
-bsd_sendpkt(cookie, pkt)
- void *cookie;
- pkt_t *pkt;
-{
- struct bsd_handle *bh = cookie;
- struct passwd *pwd;
-
- assert(bh != NULL);
- assert(pkt != NULL);
-
- /*
- * Initialize this datagram, and add the header
- */
- dgram_zero(&netfd.dgram);
- dgram_cat(&netfd.dgram, pkthdr2str(bh, pkt));
-
- /*
- * Add the security info. This depends on which kind of packet we're
- * sending.
- */
- switch (pkt->type) {
- case P_REQ:
- /*
- * Requests get sent with our username in the body
- */
- if ((pwd = getpwuid(geteuid())) == NULL) {
- security_seterror(&bh->sech,
- "can't get login name for my uid %ld", (long)getuid());
- return (-1);
- }
- dgram_cat(&netfd.dgram, "SECURITY USER %s\n", pwd->pw_name);
- break;
-
- default:
- break;
- }
-
- /*
- * Add the body, and send it
- */
- dgram_cat(&netfd.dgram, pkt->body);
- if (dgram_send_addr(bh->peer, &netfd.dgram) != 0) {
- security_seterror(&bh->sech,
- "send %s to %s failed: %s", pkt_type2str(pkt->type),
- bh->hostname, strerror(errno));
- return (-1);
- }
- return (0);
-}
-
-/*
- * Set up to receive a packet asynchronously, and call back when it has
- * been read.
- */
-static void
-bsd_recvpkt(cookie, fn, arg, timeout)
- void *cookie, *arg;
- void (*fn) P((void *, pkt_t *, security_status_t));
- int timeout;
-{
- struct bsd_handle *bh = cookie;
-
- assert(bh != NULL);
- assert(fn != NULL);
-
-
- /*
- * Subsequent recvpkt calls override previous ones
- */
- if (bh->ev_read == NULL) {
- netfd_addref();
- bh->ev_read = event_register(bh->event_id, EV_WAIT,
- recvpkt_callback, bh);
- }
- if (bh->ev_timeout != NULL)
- event_release(bh->ev_timeout);
- if (timeout < 0)
- bh->ev_timeout = NULL;
- else
- bh->ev_timeout = event_register(timeout, EV_TIME, recvpkt_timeout, bh);
- bh->fn = fn;
- bh->arg = arg;
-}
-
-/*
- * Remove a async receive request on this handle from the queue.
- * If it is the last one to be removed, then remove the event
- * handler for our network fd
- */
-static void
-bsd_recvpkt_cancel(cookie)
- void *cookie;
-{
- struct bsd_handle *bh = cookie;
-
- assert(bh != NULL);
-
- if (bh->ev_read != NULL) {
- netfd_delref();
- event_release(bh->ev_read);
- bh->ev_read = NULL;
- }
-
- if (bh->ev_timeout != NULL) {
- event_release(bh->ev_timeout);
- bh->ev_timeout = NULL;
- }
-}
-
-/*
- * Callback for received packets. This is the function bsd_recvpkt
- * registers with the event handler. It is called when the event handler
- * realizes that data is waiting to be read on the network socket.
- */
-static void
-netfd_read_callback(cookie)
- void *cookie;
-{
- struct bsd_handle *bh;
- struct hostent *he;
- int a;
-
- assert(cookie == NULL);
-
-#ifndef TEST /* { */
- /*
- * Receive the packet.
- */
- dgram_zero(&netfd.dgram);
- if (dgram_recv(&netfd.dgram, 0, &netfd.peer) < 0)
- return;
-#endif /* !TEST */ /* } */
-
- /*
- * Parse the packet.
- */
- if (str2pkthdr() < 0)
- return;
-
- /*
- * If there are events waiting on this handle, we're done
- */
- bh = bh_first;
- while(bh != NULL && (strcmp(bh->proto_handle, netfd.handle) != 0 ||
- bh->sequence != netfd.sequence ||
- bh->peer.sin_addr.s_addr != netfd.peer.sin_addr.s_addr ||
- bh->peer.sin_port != netfd.peer.sin_port)) {
- bh = bh->next;
- }
- if (bh && event_wakeup(bh->event_id) > 0)
- return;
-
- /*
- * If we didn't find a handle, then check for a new incoming packet.
- * If no accept handler was setup, then just return.
- */
- if (accept_fn == NULL)
- return;
-
- he = gethostbyaddr((void *)&netfd.peer.sin_addr,
- (int)sizeof(netfd.peer.sin_addr), AF_INET);
- if (he == NULL)
- return;
- bh = alloc(sizeof(*bh));
- bh->proto_handle=NULL;
- security_handleinit(&bh->sech, &bsd_security_driver);
- a = inithandle(bh,
- he,
- netfd.peer.sin_port,
- netfd.handle,
- netfd.sequence);
- if (a < 0) {
- if(bh->next) {
- bh->next->prev = bh->prev;
- }
- else {
- bh_last = bh->prev;
- }
- if(bh->prev) {
- bh->prev->next = bh->next;
- }
- else {
- bh_first = bh->prev;
- }
-
- bsdprintf(("%s: closeX handle '%s'\n",
- debug_prefix_time(NULL), bh->proto_handle));
-
- amfree(bh);
- return;
- }
- /*
- * Check the security of the packet. If it is bad, then pass NULL
- * to the accept function instead of a packet.
- */
- if (recv_security_ok(bh) < 0)
- (*accept_fn)(&bh->sech, NULL);
- else
- (*accept_fn)(&bh->sech, &netfd.pkt);
-}
-
-/*
- * This is called when a handle is woken up because data read off of the
- * net is for it.
- */
-static void
-recvpkt_callback(cookie)
- void *cookie;
-{
- struct bsd_handle *bh = cookie;
- void (*fn) P((void *, pkt_t *, security_status_t));
- void *arg;
-
- assert(bh != NULL);
- bsdprintf(("%s: receive handle '%s' netfd '%s'\n",
- debug_prefix_time(NULL), bh->proto_handle,netfd.handle));
-
- if(strcmp(bh->proto_handle,netfd.handle) != 0) assert(1);
-
- /* if it didn't come from the same host/port, forget it */
- if (memcmp(&bh->peer.sin_addr, &netfd.peer.sin_addr,
- sizeof(netfd.peer.sin_addr)) != 0 ||
- bh->peer.sin_port != netfd.peer.sin_port) {
- netfd.handle = NULL;
- return;
- }
-
- /*
- * We need to cancel the recvpkt request before calling the callback
- * because the callback may reschedule us.
- */
- fn = bh->fn;
- arg = bh->arg;
- bsd_recvpkt_cancel(bh);
-
- /*
- * Check the security of the packet. If it is bad, then pass NULL
- * to the packet handling function instead of a packet.
- */
- if (recv_security_ok(bh) < 0)
- (*fn)(arg, NULL, S_ERROR);
- else
- (*fn)(arg, &netfd.pkt, S_OK);
-}
-
-/*
- * This is called when a handle times out before receiving a packet.
- */
-static void
-recvpkt_timeout(cookie)
- void *cookie;
-{
- struct bsd_handle *bh = cookie;
- void (*fn) P((void *, pkt_t *, security_status_t));
- void *arg;
-
- assert(bh != NULL);
-
- assert(bh->ev_timeout != NULL);
- fn = bh->fn;
- arg = bh->arg;
- bsd_recvpkt_cancel(bh);
- (*fn)(arg, NULL, S_TIMEOUT);
-
-}
-
-/*
- * Check the security of a received packet. Returns negative on security
- * violation, or returns 0 if ok. Removes the security info from the pkt_t.
- */
-static int
-recv_security_ok(bh)
- struct bsd_handle *bh;
-{
- char *tok, *security, *body, *result;
- pkt_t *pkt = &netfd.pkt;
-
- /*
- * Set this preempively before we mangle the body.
- */
- security_seterror(&bh->sech,
- "bad SECURITY line: '%s'", pkt->body);
-
- /*
- * Now, find the SECURITY line in the body, and parse it out
- * into an argv.
- */
- if (strncmp(pkt->body, "SECURITY", sizeof("SECURITY") - 1) == 0) {
- tok = strtok(pkt->body, " ");
- assert(strcmp(tok, "SECURITY") == 0);
- /* security info goes until the newline */
- security = strtok(NULL, "\n");
- body = strtok(NULL, "");
- /*
- * If the body is f-ked, then try to recover
- */
- if (body == NULL) {
- if (security != NULL)
- body = security + strlen(security) + 2;
- else
- body = pkt->body;
- }
- } else {
- security = NULL;
- body = pkt->body;
- }
-
- /*
- * We need to do different things depending on which type of packet
- * this is.
- */
- switch (pkt->type) {
- case P_REQ:
- /*
- * Request packets must come from a reserved port
- */
- if (ntohs(bh->peer.sin_port) >= IPPORT_RESERVED) {
- security_seterror(&bh->sech,
- "host %s: port %d not secure", bh->hostname,
- ntohs(bh->peer.sin_port));
- return (-1);
- }
-
- /*
- * Request packets contain a remote username. We need to check
- * that we allow it in.
- *
- * They will look like:
- * SECURITY USER [username]
- */
-
- /* there must be some security info */
- if (security == NULL) {
- security_seterror(&bh->sech,
- "no bsd SECURITY for P_REQ");
- return (-1);
- }
-
- /* second word must be USER */
- if ((tok = strtok(security, " ")) == NULL)
- return (-1); /* default errmsg */
- if (strcmp(tok, "USER") != 0) {
- security_seterror(&bh->sech,
- "REQ SECURITY line parse error, expecting USER, got %s", tok);
- return (-1);
- }
-
- /* the third word is the username */
- if ((tok = strtok(NULL, "")) == NULL)
- return (-1); /* default errmsg */
- if ((result = check_user(bh, tok)) != NULL) {
- security_seterror(&bh->sech, "%s", result);
- amfree(result);
- return (-1);
- }
-
- /* we're good to go */
- break;
- default:
- break;
- }
-
- /*
- * If there is security info at the front of the packet, we need to
- * shift the rest of the data up and nuke it.
- */
- if (body != pkt->body)
- memmove(pkt->body, body, strlen(body) + 1);
- return (0);
-}
-
-static char *
-check_user(bh, remoteuser)
- struct bsd_handle *bh;
- const char *remoteuser;
-{
- struct passwd *pwd;
- char *r;
- char *result = NULL;
- char *localuser = NULL;
-
- /* lookup our local user name */
- if ((pwd = getpwnam(CLIENT_LOGIN)) == NULL) {
- return vstralloc("getpwnam(", CLIENT_LOGIN, ") fails", NULL);
- }
-
- /*
- * Make a copy of the user name in case getpw* is called by
- * any of the lower level routines.
- */
- localuser = stralloc(pwd->pw_name);
-
-#ifndef USE_AMANDAHOSTS
- r = check_user_ruserok(bh->hostname, pwd, remoteuser);
-#else
- r = check_user_amandahosts(bh->hostname, pwd, remoteuser);
-#endif
- if (r != NULL) {
- result = vstralloc("access as ", localuser, " not allowed",
- " from ", remoteuser, "@", bh->hostname,
- ": ", r,
- NULL);
- amfree(r);
- }
- amfree(localuser);
- return result;
-}
-
-/*
- * See if a remote user is allowed in. This version uses ruserok()
- * and friends.
- *
- * Returns 0 on success, or negative on error.
- */
-char *
-check_user_ruserok(host, pwd, remoteuser)
- const char *host;
- struct passwd *pwd;
- const char *remoteuser;
-{
- int saved_stderr;
- int fd[2];
- FILE *fError;
- amwait_t exitcode;
- pid_t ruserok_pid;
- pid_t pid;
- char *es;
- char *result;
- int ok;
- char number[NUM_STR_SIZE];
- uid_t myuid = getuid();
-
- /*
- * note that some versions of ruserok (eg SunOS 3.2) look in
- * "./.rhosts" rather than "~CLIENT_LOGIN/.rhosts", so we have to
- * chdir ourselves. Sigh.
- *
- * And, believe it or not, some ruserok()'s try an initgroup just
- * for the hell of it. Since we probably aren't root at this point
- * it'll fail, and initgroup "helpfully" will blatt "Setgroups: Not owner"
- * into our stderr output even though the initgroup failure is not a
- * problem and is expected. Thanks a lot. Not.
- */
- if (pipe(fd) != 0) {
- return stralloc2("pipe() fails: ", strerror(errno));
- }
- if ((ruserok_pid = fork()) < 0) {
- return stralloc2("fork() fails: ", strerror(errno));
- } else if (ruserok_pid == 0) {
- int ec;
-
- close(fd[0]);
- fError = fdopen(fd[1], "w");
- /* pamper braindead ruserok's */
- if (chdir(pwd->pw_dir) != 0) {
- fprintf(fError, "chdir(%s) failed: %s",
- pwd->pw_dir, strerror(errno));
- fclose(fError);
- exit(1);
- }
-
-#if defined(SHOW_SECURITY_DETAIL) /* { */
- {
- char *dir = stralloc(pwd->pw_dir);
-
- bsdprintf(("%s: calling ruserok(%s, %d, %s, %s)\n",
- debug_prefix_time(NULL),
- host, myuid == 0, remoteuser, pwd->pw_name));
- if (myuid == 0) {
- bsdprintf(("%s: because you are running as root, ",
- debug_prefix(NULL)));
- bsdprintf(("/etc/hosts.equiv will not be used\n"));
- } else {
- show_stat_info("/etc/hosts.equiv", NULL);
- }
- show_stat_info(dir, "/.rhosts");
- amfree(dir);
- }
-#endif /* } */
-
- saved_stderr = dup(2);
- close(2);
- if (open("/dev/null", O_RDWR) == -1) {
- dbprintf(("Could not open /dev/null: %s\n",
- strerror(errno)));
- ec = 1;
- } else {
- ok = ruserok(host, myuid == 0, remoteuser, CLIENT_LOGIN);
- if (ok < 0) {
- ec = 1;
- } else {
- ec = 0;
- }
- }
- (void)dup2(saved_stderr,2);
- close(saved_stderr);
- exit(ec);
- }
- close(fd[1]);
- fError = fdopen(fd[0], "r");
-
- result = NULL;
- while ((es = agets(fError)) != NULL) {
- if (result == NULL) {
- result = stralloc("");
- } else {
- strappend(result, ": ");
- }
- strappend(result, es);
- }
- close(fd[0]);
-
- while (1) {
- if ((pid = wait(&exitcode)) == (pid_t) -1) {
- if (errno == EINTR) {
- continue;
- }
- amfree(result);
- return stralloc2("ruserok wait failed: %s", strerror(errno));
- }
- if (pid == ruserok_pid) {
- break;
- }
- }
- if (WIFSIGNALED(exitcode)) {
- amfree(result);
- snprintf(number, sizeof(number), "%d", WTERMSIG(exitcode));
- return stralloc2("ruserok child got signal ", number);
- }
- if (WEXITSTATUS(exitcode) == 0) {
- amfree(result);
- } else if (result == NULL) {
- result = stralloc("ruserok failed");
- }
-
- return result;
-}
-
-/*
- * Check to see if a user is allowed in. This version uses .amandahosts
- * Returns -1 on failure, or 0 on success.
- */
-char *
-check_user_amandahosts(host, pwd, remoteuser)
- const char *host;
- struct passwd *pwd;
- const char *remoteuser;
-{
- char *line = NULL;
- char *filehost;
- const char *fileuser;
- char *ptmp = NULL;
- char *result = NULL;
- FILE *fp = NULL;
- int found;
- struct stat sbuf;
- char n1[NUM_STR_SIZE];
- char n2[NUM_STR_SIZE];
- int hostmatch;
- int usermatch;
- uid_t localuid;
- char *localuser = NULL;
-
- /*
- * Save copies of what we need from the passwd structure in case
- * any other code calls getpw*.
- */
- localuid = pwd->pw_uid;
- localuser = stralloc(pwd->pw_name);
-
- ptmp = stralloc2(pwd->pw_dir, "/.amandahosts");
-#if defined(SHOW_SECURITY_DETAIL) /* { */
- show_stat_info(ptmp, "");;
-#endif /* } */
- if ((fp = fopen(ptmp, "r")) == NULL) {
- result = vstralloc("cannot open ", ptmp, ": ", strerror(errno), NULL);
- amfree(ptmp);
- amfree(localuser);
- return result;
- }
-
- /*
- * Make sure the file is owned by the Amanda user and does not
- * have any group/other access allowed.
- */
- if (fstat(fileno(fp), &sbuf) != 0) {
- result = vstralloc("cannot fstat ", ptmp, ": ", strerror(errno), NULL);
- goto common_exit;
- }
- if (sbuf.st_uid != localuid) {
- snprintf(n1, sizeof(n1), "%ld", (long)sbuf.st_uid);
- snprintf(n2, sizeof(n2), "%ld", (long)localuid);
- result = vstralloc(ptmp, ": ",
- "owned by id ", n1,
- ", should be ", n2,
- NULL);
- goto common_exit;
- }
- if ((sbuf.st_mode & 077) != 0) {
- result = stralloc2(ptmp,
- ": incorrect permissions; file must be accessible only by its owner");
- goto common_exit;
- }
-
- /*
- * Now, scan the file for the host/user.
- */
- found = 0;
- while ((line = agets(fp)) != NULL) {
-#if defined(SHOW_SECURITY_DETAIL) /* { */
- bsdprintf(("%s: processing line: <%s>\n", debug_prefix(NULL), line));
-#endif /* } */
- /* get the host out of the file */
- if ((filehost = strtok(line, " \t")) == NULL) {
- amfree(line);
- continue;
- }
-
- /* get the username. If no user specified, then use the local user */
- if ((fileuser = strtok(NULL, " \t")) == NULL) {
- fileuser = localuser;
- }
-
- hostmatch = (strcasecmp(filehost, host) == 0);
- usermatch = (strcasecmp(fileuser, remoteuser) == 0);
-#if defined(SHOW_SECURITY_DETAIL) /* { */
- bsdprintf(("%s: comparing \"%s\" with\n", debug_prefix(NULL), filehost));
- bsdprintf(("%s: \"%s\" (%s)\n", host,
- debug_prefix(NULL), hostmatch ? "match" : "no match"));
- bsdprintf(("%s: and \"%s\" with\n", fileuser, debug_prefix(NULL)));
- bsdprintf(("%s: \"%s\" (%s)\n", remoteuser,
- debug_prefix(NULL), usermatch ? "match" : "no match"));
-#endif /* } */
- amfree(line);
- /* compare */
- if (hostmatch && usermatch) {
- /* success */
- found = 1;
- break;
- }
- }
- if (! found) {
- result = vstralloc(ptmp, ": ",
- "\"", host, " ", remoteuser, "\"",
- " entry not found",
- NULL);
- }
-
-common_exit:
-
- afclose(fp);
- amfree(ptmp);
- amfree(line);
- amfree(localuser);
-
- return result;
-}
-
-/* return 1 on success, 0 on failure */
-int
-check_security(addr, str, cksum, errstr)
-struct sockaddr_in *addr;
-char *str;
-unsigned long cksum;
-char **errstr;
-{
- char *remotehost = NULL, *remoteuser = NULL;
- char *bad_bsd = NULL;
- struct hostent *hp;
- struct passwd *pwptr;
- int myuid, i, j;
- char *s, *fp;
- int ch;
-
- *errstr = NULL;
-
- /* what host is making the request? */
-
- hp = gethostbyaddr((char *)&addr->sin_addr, sizeof(addr->sin_addr),
- AF_INET);
- if(hp == NULL) {
- /* XXX include remote address in message */
- *errstr = vstralloc("[",
- "addr ", inet_ntoa(addr->sin_addr), ": ",
- "hostname lookup failed",
- "]", NULL);
- return 0;
- }
- remotehost = stralloc(hp->h_name);
-
- /* Now let's get the hostent for that hostname */
- hp = gethostbyname( remotehost );
- if(hp == NULL) {
- /* XXX include remote hostname in message */
- *errstr = vstralloc("[",
- "host ", remotehost, ": ",
- "hostname lookup failed",
- "]", NULL);
- amfree(remotehost);
- return 0;
- }
-
- /* Verify that the hostnames match -- they should theoretically */
- if( strncasecmp( remotehost, hp->h_name, strlen(remotehost)+1 ) != 0 ) {
- *errstr = vstralloc("[",
- "hostnames do not match: ",
- remotehost, " ", hp->h_name,
- "]", NULL);
- amfree(remotehost);
- return 0;
- }
-
- /* Now let's verify that the ip which gave us this hostname
- * is really an ip for this hostname; or is someone trying to
- * break in? (THIS IS THE CRUCIAL STEP)
- */
- for (i = 0; hp->h_addr_list[i]; i++) {
- if (memcmp(hp->h_addr_list[i],
- (char *) &addr->sin_addr, sizeof(addr->sin_addr)) == 0)
- break; /* name is good, keep it */
- }
-
- /* If we did not find it, your DNS is messed up or someone is trying
- * to pull a fast one on you. :(
- */
-
- /* Check even the aliases list. Work around for Solaris if dns goes over NIS */
-
- if( !hp->h_addr_list[i] ) {
- for (j = 0; hp->h_aliases[j] !=0 ; j++) {
- if ( strcmp(hp->h_aliases[j],inet_ntoa(addr->sin_addr)) == 0)
- break; /* name is good, keep it */
- }
- if( !hp->h_aliases[j] ) {
- *errstr = vstralloc("[",
- "ip address ", inet_ntoa(addr->sin_addr),
- " is not in the ip list for ", remotehost,
- "]",
- NULL);
- amfree(remotehost);
- return 0;
- }
- }
-
- /* next, make sure the remote port is a "reserved" one */
-
- if(ntohs(addr->sin_port) >= IPPORT_RESERVED) {
- char number[NUM_STR_SIZE];
-
- snprintf(number, sizeof(number), "%d", ntohs(addr->sin_port));
- *errstr = vstralloc("[",
- "host ", remotehost, ": ",
- "port ", number, " not secure",
- "]", NULL);
- amfree(remotehost);
- return 0;
- }
-
- /* extract the remote user name from the message */
-
- s = str;
- ch = *s++;
-
- bad_bsd = vstralloc("[",
- "host ", remotehost, ": ",
- "bad bsd security line",
- "]", NULL);
-
-#define sc "USER "
- if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
- *errstr = bad_bsd;
- bad_bsd = NULL;
- amfree(remotehost);
- return 0;
- }
- s += sizeof(sc)-1;
- ch = s[-1];
-#undef sc
-
- skip_whitespace(s, ch);
- if(ch == '\0') {
- *errstr = bad_bsd;
- bad_bsd = NULL;
- amfree(remotehost);
- return 0;
- }
- fp = s - 1;
- skip_non_whitespace(s, ch);
- s[-1] = '\0';
- remoteuser = stralloc(fp);
- s[-1] = ch;
- amfree(bad_bsd);
-
- /* lookup our local user name */
-
- myuid = getuid();
- if((pwptr = getpwuid(myuid)) == NULL)
- error("error [getpwuid(%d) fails]", myuid);
-
- dbprintf(("bsd security: remote host %s user %s local user %s\n",
- remotehost, remoteuser, pwptr->pw_name));
-
-#ifndef USE_AMANDAHOSTS
- s = check_user_ruserok(remotehost, pwptr, remoteuser);
-#else
- s = check_user_amandahosts(remotehost, pwptr, remoteuser);
-#endif
-
- if (s != NULL) {
- *errstr = vstralloc("[",
- "access as ", pwptr->pw_name, " not allowed",
- " from ", remoteuser, "@", remotehost,
- ": ", s, "]", NULL);
- }
- amfree(s);
- amfree(remotehost);
- amfree(remoteuser);
- return *errstr == NULL;
-}
-
-