From 650fe0907f39b13780c222ec3a8a9fc7cf299884 Mon Sep 17 00:00:00 2001 From: dwmalone Date: Mon, 4 Jun 2001 11:43:29 +0000 Subject: This patch cleans up the ident stuff in inetd. The code which has been patched so many times it was a bit of a mess. There are style, code and man page cleanups. The following are the functional changes: The RFC only permits the returning of 4 possible error codes, make sure we only return these (PR 27636). Use MAXLOGNAME to determine the longest usernames. Add a -i flag, which returns the uid instead of the username (this is from a PR 25787, which also contained alot of the cleanups in this patch). PR: 25787, 27636 Partially Submitted by: Arne.Dag.Fidjestol@idi.ntnu.no Reviewed by: Arne.Dag.Fidjestol@idi.ntnu.no, green MFC after: 3 weeks --- usr.sbin/inetd/builtins.c | 229 ++++++++++++++++++++++++---------------------- usr.sbin/inetd/inetd.8 | 37 +++++--- usr.sbin/inetd/inetd.h | 2 +- 3 files changed, 145 insertions(+), 123 deletions(-) (limited to 'usr.sbin') diff --git a/usr.sbin/inetd/builtins.c b/usr.sbin/inetd/builtins.c index 49cd12d..88435a0 100644 --- a/usr.sbin/inetd/builtins.c +++ b/usr.sbin/inetd/builtins.c @@ -306,15 +306,21 @@ echo_stream(s, sep) /* Echo service -- echo data back */ * support. */ +/* RFC 1413 says the following are the only errors you can return. */ +#define ID_INVALID "INVALID-PORT" /* Port number improperly specified. */ +#define ID_NOUSER "NO-USER" /* Port not in use/not identifable. */ +#define ID_HIDDEN "HIDDEN-USER" /* Hiden at user's request. */ +#define ID_UNKNOWN "UNKNOWN-ERROR" /* Everything else. */ + /* ARGSUSED */ void iderror(lport, fport, s, er) /* Generic ident_stream error-sending func */ - int lport, fport, s, er; + int lport, fport, s; + char *er; { char *p; - asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, - er == -1 ? "HIDDEN-USER" : er ? strerror(er) : "UNKNOWN-ERROR"); + asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, er); if (p == NULL) { syslog(LOG_ERR, "asprintf: %m"); exit(EX_OSERR); @@ -345,13 +351,13 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */ }, to; struct passwd *pw = NULL; fd_set fdset; - char buf[BUFSIZE], *cp = NULL, *p, **av, *osname = NULL, garbage[7], e; - char *fallback = NULL; + char buf[BUFSIZE], *p, **av, *osname = NULL, e; + char idbuf[MAXLOGNAME] = ""; /* Big enough to hold uid in decimal. */ socklen_t socklen; ssize_t ssize; size_t size, bufsiz; - int c, fflag = 0, nflag = 0, rflag = 0, argc = 0, usedfallback = 0; - int gflag = 0, Fflag = 0, getcredfail = 0, onreadlen; + int c, fflag = 0, nflag = 0, rflag = 0, argc = 0; + int gflag = 0, iflag = 0, Fflag = 0, getcredfail = 0, onreadlen; u_short lport, fport; inetd_setproctitle(sep->se_service, s); @@ -373,10 +379,11 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */ size_t i; u_int32_t random; - while ((c = getopt(argc, sep->se_argv, "d:fFgno:rt:")) != -1) + while ((c = getopt(argc, sep->se_argv, "d:fFgino:rt:")) != -1) switch (c) { case 'd': - fallback = optarg; + if (!gflag) + strlcpy(idbuf, optarg, sizeof(idbuf)); break; case 'f': fflag = 1; @@ -394,21 +401,22 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */ * gives a more optimal way to reload the * random number only when necessary. * - * I'm using base-36, so I need at least 6 - * bits; round it up to 8 bits to make it - * easier. + * 32 bits from arc4random corrisponds to + * about 6 base-36 digits, so we reseed evey 6. */ - for (i = 0; i < sizeof(garbage) - 1; i++) { - const char *const base36 = + for (i = 0; i < sizeof(idbuf) - 1; i++) { + static const char *const base36 = "0123456789" "abcdefghijklmnopqrstuvwxyz"; - if (i % (sizeof(random) * 8 / 8) == 0) + if (i % 6 == 0) random = arc4random(); - garbage[i] = - base36[(random & 0xff) % 36]; - random >>= 8; + idbuf[i] = base36[random % 36]; + random /= 36; } - garbage[i] = '\0'; + idbuf[i] = '\0'; + break; + case 'i': + iflag = 1; break; case 'n': nflag = 1; @@ -423,6 +431,7 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */ switch (sscanf(optarg, "%d.%d", &sec, &usec)) { case 2: tv.tv_usec = usec; + /* FALLTHROUGH */ case 1: tv.tv_sec = sec; break; @@ -438,15 +447,10 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */ } if (osname == NULL) { if (uname(&un) == -1) - iderror(0, 0, s, errno); + iderror(0, 0, s, ID_UNKNOWN); osname = un.sysname; } - socklen = sizeof(ss[0]); - if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1) - iderror(0, 0, s, errno); - socklen = sizeof(ss[1]); - if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1) - iderror(0, 0, s, errno); + /* * We're going to prepare for and execute reception of a * packet of data from the user. The data is in the format @@ -476,14 +480,14 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */ break; FD_SET(s, &fdset); if (select(s + 1, &fdset, NULL, NULL, &tv) == -1) - iderror(0, 0, s, errno); + iderror(0, 0, s, ID_UNKNOWN); if (ioctl(s, FIONREAD, &onreadlen) == -1) - iderror(0, 0, s, errno); + iderror(0, 0, s, ID_UNKNOWN); if (onreadlen > bufsiz) onreadlen = bufsiz; ssize = read(s, &buf[size], (size_t)onreadlen); if (ssize == -1) - iderror(0, 0, s, errno); + iderror(0, 0, s, ID_UNKNOWN); else if (ssize == 0) break; bufsiz -= ssize; @@ -494,37 +498,38 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */ buf[size] = '\0'; /* Read two characters, and check for a delimiting character */ if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e)) - iderror(0, 0, s, 0); - if (gflag) { - cp = garbage; + iderror(0, 0, s, ID_INVALID); + + /* Send garbage? */ + if (gflag) goto printit; - } - + /* * If not "real" (-r), send a HIDDEN-USER error for everything. * If -d is used to set a fallback username, this is used to * override it, and the fallback is returned instead. */ if (!rflag) { - if (fallback == NULL) - iderror(lport, fport, s, -1); - else { - cp = fallback; - goto printit; - } + if (*idbuf == '\0') + iderror(lport, fport, s, ID_HIDDEN); + goto printit; } - + /* * We take the input and construct an array of two sockaddr_ins * which contain the local address information and foreign * address information, respectively, used to look up the * credentials for the socket (which are returned by the - * sysctl "net.inet.tcp.getcred" when we call it.) The - * arrays have been filled in above via get{peer,sock}name(), - * so right here we are only setting the ports. + * sysctl "net.inet.tcp.getcred" when we call it.) */ + socklen = sizeof(ss[0]); + if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1) + iderror(lport, fport, s, ID_UNKNOWN); + socklen = sizeof(ss[1]); + if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1) + iderror(lport, fport, s, ID_UNKNOWN); if (ss[0].ss_family != ss[1].ss_family) - iderror(lport, fport, s, EINVAL); + iderror(lport, fport, s, ID_UNKNOWN); size = sizeof(uc); switch (ss[0].ss_family) { case AF_INET: @@ -552,48 +557,54 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */ break; } if (getcredfail != 0) { - if (fallback == NULL) /* Use a default, if asked to */ - iderror(lport, fport, s, getcredfail); - usedfallback = 1; - } else { - /* Look up the pw to get the username */ - errno = 0; - pw = getpwuid(uc.cr_uid); + if (*idbuf == '\0') + iderror(lport, fport, s, + getcredfail == ENOENT ? ID_NOUSER : ID_UNKNOWN); + goto printit; } - if (pw == NULL && !usedfallback) /* No such user... */ - iderror(lport, fport, s, errno != 0 ? errno : ENOENT); + + /* Look up the pw to get the username and home directory*/ + errno = 0; + pw = getpwuid(uc.cr_uid); + if (pw == NULL) + iderror(lport, fport, s, errno == 0 ? ID_NOUSER : ID_UNKNOWN); + + if (iflag) + snprintf(idbuf, sizeof(idbuf), "%u", (unsigned)pw->pw_uid); + else + strlcpy(idbuf, pw->pw_name, sizeof(idbuf)); + /* * If enabled, we check for a file named ".noident" in the user's * home directory. If found, we return HIDDEN-USER. */ - if (nflag && !usedfallback) { + if (nflag) { if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1) - iderror(lport, fport, s, errno); + iderror(lport, fport, s, ID_UNKNOWN); if (lstat(p, &sb) == 0) { free(p); - iderror(lport, fport, s, -1); + iderror(lport, fport, s, ID_HIDDEN); } free(p); } + /* * Here, if enabled, we read a user's ".fakeid" file in their * home directory. It consists of a line containing the name * they want. */ - if (fflag && !usedfallback) { - FILE *fakeid = NULL; + if (fflag) { int fakeid_fd; - if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1) - iderror(lport, fport, s, errno); /* * Here we set ourself to effectively be the user, so we don't * open any files we have no permission to open, especially * symbolic links to sensitive root-owned files or devices. */ if (initgroups(pw->pw_name, pw->pw_gid) == -1) - iderror(lport, fport, s, errno); - seteuid(pw->pw_uid); + iderror(lport, fport, s, ID_UNKNOWN); + if (seteuid(pw->pw_uid) == -1) + iderror(lport, fport, s, ID_UNKNOWN); /* * We can't stat() here since that would be a race * condition. @@ -601,60 +612,58 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */ * and if it's not a regular file, we close it and end up * returning the user's real username. */ + if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1) + iderror(lport, fport, s, ID_UNKNOWN); fakeid_fd = open(p, O_RDONLY | O_NONBLOCK); free(p); - if (fakeid_fd != -1 && fstat(fakeid_fd, &sb) != -1 && - S_ISREG(sb.st_mode) && - (fakeid = fdopen(fakeid_fd, "r")) != NULL) { - buf[sizeof(buf) - 1] = '\0'; - if (fgets(buf, sizeof(buf), fakeid) == NULL) { - cp = pw->pw_name; - fclose(fakeid); - goto printit; - } - /* - * Usually, the file will have the desired identity - * in the form "identity\n", so we use strcspn() to - * end the string (which fgets() doesn't do.) - */ - buf[strcspn(buf, "\r\n")] = '\0'; - cp = buf; - /* Allow for beginning white space... */ - while (isspace(*cp)) - cp++; - /* ...and ending white space. */ - cp[strcspn(cp, " \t")] = '\0'; - /* User names of >16 characters are invalid */ - if (strlen(cp) > 16) - cp[16] = '\0'; - /* - * If the name is a zero-length string or matches - * the name of another user, it's invalid, so - * we will return their real identity instead. - */ - - if (!*cp || (!Fflag && getpwnam(cp))) { - errno = 0; - pw = getpwuid(uc.cr_uid); - if (pw == NULL) - iderror(lport, fport, s, - errno != 0 ? errno : ENOENT); - cp = pw->pw_name; + if (fakeid_fd == -1 || fstat(fakeid_fd, &sb) == -1 || + !S_ISREG(sb.st_mode)) + goto fakeid_fail; + + if ((ssize = read(fakeid_fd, buf, sizeof(buf) - 1)) < 0) + goto fakeid_fail; + buf[ssize] = '\0'; + + /* + * Usually, the file will have the desired identity + * in the form "identity\n". Allow for leading white + * space and trailing white space/end of line. + */ + p = buf; + p += strspn(p, " \t"); + p[strcspn(p, " \t\r\n")] = '\0'; + if (strlen(p) > MAXLOGNAME - 1) /* Too long (including nul)? */ + p[MAXLOGNAME - 1] = '\0'; + + /* + * If the name is a zero-length string or matches it + * the id or name of another user (unless permitted by -F) + * then it is invalid. + */ + if (*p == '\0') + goto fakeid_fail; + if (!Fflag) { + if (iflag) { + if (p[strspn(p, "0123456789")] == '\0' && + getpwuid(atoi(p)) != NULL) + goto fakeid_fail; + } else { + if (getpwnam(p) != NULL) + goto fakeid_fail; } - } else - cp = pw->pw_name; - if (fakeid != NULL) - fclose(fakeid); - else if (fakeid_fd != -1) + } + + strlcpy(idbuf, p, sizeof(idbuf)); + +fakeid_fail: + if (fakeid_fd != -1) close(fakeid_fd); - } else if (!usedfallback) - cp = pw->pw_name; - else - cp = fallback; + } + printit: /* Finally, we make and send the reply. */ if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname, - cp) == -1) { + idbuf) == -1) { syslog(LOG_ERR, "asprintf: %m"); exit(EX_OSERR); } diff --git a/usr.sbin/inetd/inetd.8 b/usr.sbin/inetd/inetd.8 index 3cd6e5a..fda2267 100644 --- a/usr.sbin/inetd/inetd.8 +++ b/usr.sbin/inetd/inetd.8 @@ -455,6 +455,21 @@ If the real service is disabled, return this username for every request. This is primarily useful when running this service on a NAT machine. +.It Fl g +Instead of returning +the user's name to the ident requester, +report a +username made up of random alphanumeric characters, +e.g. +.Dq c0c993 . +The +.Fl g +flag overrides not only the user names, +but also any fallback name, +.Pa .fakeid +or +.Pa .noident +files. .It Fl t Xo .Ar sec Ns Op . Ns Ar usec .Xc @@ -465,6 +480,8 @@ Offer a real .Dq auth service, as per RFC 1413. All the remaining flags apply only in this case. +.It Fl i +Return numeric user IDs instead of usernames. .It Fl f If the file .Pa .fakeid @@ -474,29 +491,25 @@ If the the username found in .Pa .fakeid is that of an existing user, then the real username is reported. +If the +.Fl i +flag is also given then the username in +.Pa .fakeid +is checked against existing user IDs instead. .It Fl F same as .Fl f but without the restriction that the username in .Pa .fakeid must not match an existing user. -.It Fl g -Instead of returning the user's name to the ident requester, report a -username made up of random alphanumeric characters, e.g. -.Dq c0c993 . -The -.Fl g -flag overrides not only the user names, but also any -.Pa .fakeid -or -.Pa .noident -files. .It Fl n If the file .Pa .noident exists in the home directory of the identified user, return .Dq ERROR\ : HIDDEN-USER . -instead. +This overrides any +.Pa fakeid +file which might exist. .It Fl o Ar osname Use .Ar osname diff --git a/usr.sbin/inetd/inetd.h b/usr.sbin/inetd/inetd.h index c0c9e64..c11f4ad 100644 --- a/usr.sbin/inetd/inetd.h +++ b/usr.sbin/inetd/inetd.h @@ -122,7 +122,7 @@ void endconfig __P((void)); struct servtab *enter __P((struct servtab *)); void freeconfig __P((struct servtab *)); struct servtab *getconfigent __P((void)); -void iderror __P((int, int, int, int)); +void iderror __P((int, int, int, char *)); void ident_stream __P((int, struct servtab *)); void machtime_dg __P((int, struct servtab *)); void machtime_stream __P((int, struct servtab *)); -- cgit v1.1