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.
*/
/* 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;
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;
}
/*
* 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;
}
} 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);
}

View file

@ -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

View file

@ -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 *));