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:
parent
0becf102d7
commit
9a0b3389d5
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 *));
|
||||
|
Loading…
x
Reference in New Issue
Block a user