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
This commit is contained in:
David Malone 2001-06-04 11:43:29 +00:00
parent 0becf102d7
commit 9a0b3389d5
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=77684
3 changed files with 145 additions and 123 deletions

View File

@ -306,15 +306,21 @@ echo_stream(s, sep) /* Echo service -- echo data back */
* support. * 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 */ /* ARGSUSED */
void void
iderror(lport, fport, s, er) /* Generic ident_stream error-sending func */ iderror(lport, fport, s, er) /* Generic ident_stream error-sending func */
int lport, fport, s, er; int lport, fport, s;
char *er;
{ {
char *p; char *p;
asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, er);
er == -1 ? "HIDDEN-USER" : er ? strerror(er) : "UNKNOWN-ERROR");
if (p == NULL) { if (p == NULL) {
syslog(LOG_ERR, "asprintf: %m"); syslog(LOG_ERR, "asprintf: %m");
exit(EX_OSERR); exit(EX_OSERR);
@ -345,13 +351,13 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */
}, to; }, to;
struct passwd *pw = NULL; struct passwd *pw = NULL;
fd_set fdset; fd_set fdset;
char buf[BUFSIZE], *cp = NULL, *p, **av, *osname = NULL, garbage[7], e; char buf[BUFSIZE], *p, **av, *osname = NULL, e;
char *fallback = NULL; char idbuf[MAXLOGNAME] = ""; /* Big enough to hold uid in decimal. */
socklen_t socklen; socklen_t socklen;
ssize_t ssize; ssize_t ssize;
size_t size, bufsiz; size_t size, bufsiz;
int c, fflag = 0, nflag = 0, rflag = 0, argc = 0, usedfallback = 0; int c, fflag = 0, nflag = 0, rflag = 0, argc = 0;
int gflag = 0, Fflag = 0, getcredfail = 0, onreadlen; int gflag = 0, iflag = 0, Fflag = 0, getcredfail = 0, onreadlen;
u_short lport, fport; u_short lport, fport;
inetd_setproctitle(sep->se_service, s); inetd_setproctitle(sep->se_service, s);
@ -373,10 +379,11 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */
size_t i; size_t i;
u_int32_t random; 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) { switch (c) {
case 'd': case 'd':
fallback = optarg; if (!gflag)
strlcpy(idbuf, optarg, sizeof(idbuf));
break; break;
case 'f': case 'f':
fflag = 1; fflag = 1;
@ -394,21 +401,22 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */
* gives a more optimal way to reload the * gives a more optimal way to reload the
* random number only when necessary. * random number only when necessary.
* *
* I'm using base-36, so I need at least 6 * 32 bits from arc4random corrisponds to
* bits; round it up to 8 bits to make it * about 6 base-36 digits, so we reseed evey 6.
* easier.
*/ */
for (i = 0; i < sizeof(garbage) - 1; i++) { for (i = 0; i < sizeof(idbuf) - 1; i++) {
const char *const base36 = static const char *const base36 =
"0123456789" "0123456789"
"abcdefghijklmnopqrstuvwxyz"; "abcdefghijklmnopqrstuvwxyz";
if (i % (sizeof(random) * 8 / 8) == 0) if (i % 6 == 0)
random = arc4random(); random = arc4random();
garbage[i] = idbuf[i] = base36[random % 36];
base36[(random & 0xff) % 36]; random /= 36;
random >>= 8;
} }
garbage[i] = '\0'; idbuf[i] = '\0';
break;
case 'i':
iflag = 1;
break; break;
case 'n': case 'n':
nflag = 1; nflag = 1;
@ -423,6 +431,7 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */
switch (sscanf(optarg, "%d.%d", &sec, &usec)) { switch (sscanf(optarg, "%d.%d", &sec, &usec)) {
case 2: case 2:
tv.tv_usec = usec; tv.tv_usec = usec;
/* FALLTHROUGH */
case 1: case 1:
tv.tv_sec = sec; tv.tv_sec = sec;
break; break;
@ -438,15 +447,10 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */
} }
if (osname == NULL) { if (osname == NULL) {
if (uname(&un) == -1) if (uname(&un) == -1)
iderror(0, 0, s, errno); iderror(0, 0, s, ID_UNKNOWN);
osname = un.sysname; 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 * We're going to prepare for and execute reception of a
* packet of data from the user. The data is in the format * 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; break;
FD_SET(s, &fdset); FD_SET(s, &fdset);
if (select(s + 1, &fdset, NULL, NULL, &tv) == -1) 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) if (ioctl(s, FIONREAD, &onreadlen) == -1)
iderror(0, 0, s, errno); iderror(0, 0, s, ID_UNKNOWN);
if (onreadlen > bufsiz) if (onreadlen > bufsiz)
onreadlen = bufsiz; onreadlen = bufsiz;
ssize = read(s, &buf[size], (size_t)onreadlen); ssize = read(s, &buf[size], (size_t)onreadlen);
if (ssize == -1) if (ssize == -1)
iderror(0, 0, s, errno); iderror(0, 0, s, ID_UNKNOWN);
else if (ssize == 0) else if (ssize == 0)
break; break;
bufsiz -= ssize; bufsiz -= ssize;
@ -494,37 +498,38 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */
buf[size] = '\0'; buf[size] = '\0';
/* Read two characters, and check for a delimiting character */ /* Read two characters, and check for a delimiting character */
if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e)) if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e))
iderror(0, 0, s, 0); iderror(0, 0, s, ID_INVALID);
if (gflag) {
cp = garbage; /* Send garbage? */
if (gflag)
goto printit; goto printit;
}
/* /*
* If not "real" (-r), send a HIDDEN-USER error for everything. * If not "real" (-r), send a HIDDEN-USER error for everything.
* If -d is used to set a fallback username, this is used to * If -d is used to set a fallback username, this is used to
* override it, and the fallback is returned instead. * override it, and the fallback is returned instead.
*/ */
if (!rflag) { if (!rflag) {
if (fallback == NULL) if (*idbuf == '\0')
iderror(lport, fport, s, -1); iderror(lport, fport, s, ID_HIDDEN);
else { goto printit;
cp = fallback;
goto printit;
}
} }
/* /*
* We take the input and construct an array of two sockaddr_ins * We take the input and construct an array of two sockaddr_ins
* which contain the local address information and foreign * which contain the local address information and foreign
* address information, respectively, used to look up the * address information, respectively, used to look up the
* credentials for the socket (which are returned by the * credentials for the socket (which are returned by the
* sysctl "net.inet.tcp.getcred" when we call it.) The * sysctl "net.inet.tcp.getcred" when we call it.)
* arrays have been filled in above via get{peer,sock}name(),
* so right here we are only setting the ports.
*/ */
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) if (ss[0].ss_family != ss[1].ss_family)
iderror(lport, fport, s, EINVAL); iderror(lport, fport, s, ID_UNKNOWN);
size = sizeof(uc); size = sizeof(uc);
switch (ss[0].ss_family) { switch (ss[0].ss_family) {
case AF_INET: case AF_INET:
@ -552,48 +557,54 @@ ident_stream(s, sep) /* Ident service (AKA "auth") */
break; break;
} }
if (getcredfail != 0) { if (getcredfail != 0) {
if (fallback == NULL) /* Use a default, if asked to */ if (*idbuf == '\0')
iderror(lport, fport, s, getcredfail); iderror(lport, fport, s,
usedfallback = 1; getcredfail == ENOENT ? ID_NOUSER : ID_UNKNOWN);
} else { goto printit;
/* Look up the pw to get the username */
errno = 0;
pw = getpwuid(uc.cr_uid);
} }
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 * If enabled, we check for a file named ".noident" in the user's
* home directory. If found, we return HIDDEN-USER. * home directory. If found, we return HIDDEN-USER.
*/ */
if (nflag && !usedfallback) { if (nflag) {
if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1) 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) { if (lstat(p, &sb) == 0) {
free(p); free(p);
iderror(lport, fport, s, -1); iderror(lport, fport, s, ID_HIDDEN);
} }
free(p); free(p);
} }
/* /*
* Here, if enabled, we read a user's ".fakeid" file in their * Here, if enabled, we read a user's ".fakeid" file in their
* home directory. It consists of a line containing the name * home directory. It consists of a line containing the name
* they want. * they want.
*/ */
if (fflag && !usedfallback) { if (fflag) {
FILE *fakeid = NULL;
int fakeid_fd; 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 * Here we set ourself to effectively be the user, so we don't
* open any files we have no permission to open, especially * open any files we have no permission to open, especially
* symbolic links to sensitive root-owned files or devices. * symbolic links to sensitive root-owned files or devices.
*/ */
if (initgroups(pw->pw_name, pw->pw_gid) == -1) if (initgroups(pw->pw_name, pw->pw_gid) == -1)
iderror(lport, fport, s, errno); iderror(lport, fport, s, ID_UNKNOWN);
seteuid(pw->pw_uid); if (seteuid(pw->pw_uid) == -1)
iderror(lport, fport, s, ID_UNKNOWN);
/* /*
* We can't stat() here since that would be a race * We can't stat() here since that would be a race
* condition. * 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 * and if it's not a regular file, we close it and end up
* returning the user's real username. * 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); fakeid_fd = open(p, O_RDONLY | O_NONBLOCK);
free(p); free(p);
if (fakeid_fd != -1 && fstat(fakeid_fd, &sb) != -1 && if (fakeid_fd == -1 || fstat(fakeid_fd, &sb) == -1 ||
S_ISREG(sb.st_mode) && !S_ISREG(sb.st_mode))
(fakeid = fdopen(fakeid_fd, "r")) != NULL) { goto fakeid_fail;
buf[sizeof(buf) - 1] = '\0';
if (fgets(buf, sizeof(buf), fakeid) == NULL) { if ((ssize = read(fakeid_fd, buf, sizeof(buf) - 1)) < 0)
cp = pw->pw_name; goto fakeid_fail;
fclose(fakeid); buf[ssize] = '\0';
goto printit;
/*
* 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;
} }
/* }
* Usually, the file will have the desired identity
* in the form "identity\n", so we use strcspn() to strlcpy(idbuf, p, sizeof(idbuf));
* end the string (which fgets() doesn't do.)
*/ fakeid_fail:
buf[strcspn(buf, "\r\n")] = '\0'; if (fakeid_fd != -1)
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;
}
} else
cp = pw->pw_name;
if (fakeid != NULL)
fclose(fakeid);
else if (fakeid_fd != -1)
close(fakeid_fd); close(fakeid_fd);
} else if (!usedfallback) }
cp = pw->pw_name;
else
cp = fallback;
printit: printit:
/* Finally, we make and send the reply. */ /* Finally, we make and send the reply. */
if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname, if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname,
cp) == -1) { idbuf) == -1) {
syslog(LOG_ERR, "asprintf: %m"); syslog(LOG_ERR, "asprintf: %m");
exit(EX_OSERR); exit(EX_OSERR);
} }

View File

@ -455,6 +455,21 @@ If the real
service is disabled, service is disabled,
return this username for every request. return this username for every request.
This is primarily useful when running this service on a NAT machine. 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 .It Fl t Xo
.Ar sec Ns Op . Ns Ar usec .Ar sec Ns Op . Ns Ar usec
.Xc .Xc
@ -465,6 +480,8 @@ Offer a real
.Dq auth .Dq auth
service, as per RFC 1413. service, as per RFC 1413.
All the remaining flags apply only in this case. All the remaining flags apply only in this case.
.It Fl i
Return numeric user IDs instead of usernames.
.It Fl f .It Fl f
If the file If the file
.Pa .fakeid .Pa .fakeid
@ -474,29 +491,25 @@ If the the username found in
.Pa .fakeid .Pa .fakeid
is that of an existing user, is that of an existing user,
then the real username is reported. 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 .It Fl F
same as same as
.Fl f .Fl f
but without the restriction that the username in but without the restriction that the username in
.Pa .fakeid .Pa .fakeid
must not match an existing user. 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 .It Fl n
If the file If the file
.Pa .noident .Pa .noident
exists in the home directory of the identified user, return exists in the home directory of the identified user, return
.Dq ERROR\ : HIDDEN-USER . .Dq ERROR\ : HIDDEN-USER .
instead. This overrides any
.Pa fakeid
file which might exist.
.It Fl o Ar osname .It Fl o Ar osname
Use Use
.Ar osname .Ar osname

View File

@ -122,7 +122,7 @@ void endconfig __P((void));
struct servtab *enter __P((struct servtab *)); struct servtab *enter __P((struct servtab *));
void freeconfig __P((struct servtab *)); void freeconfig __P((struct servtab *));
struct servtab *getconfigent __P((void)); 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 ident_stream __P((int, struct servtab *));
void machtime_dg __P((int, struct servtab *)); void machtime_dg __P((int, struct servtab *));
void machtime_stream __P((int, struct servtab *)); void machtime_stream __P((int, struct servtab *));