mirror of
git://git.musl-libc.org/musl
synced 2024-12-25 08:02:28 +00:00
support alternate backends for the passwd and group dbs
when we fail to find the entry in the commonly accepted files, we query a server over a Unix domain socket on /var/run/nscd/socket. the protocol used here is compatible with glibc's nscd protocol on most systems (all that use 32-bit numbers for all the protocol fields, which appears to be everything but Alpha).
This commit is contained in:
parent
0afef1aa24
commit
34b423d207
@ -1,5 +1,21 @@
|
||||
#include <pthread.h>
|
||||
#include <byteswap.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "pwf.h"
|
||||
#include "nscd.h"
|
||||
|
||||
static char *itoa(char *p, uint32_t x)
|
||||
{
|
||||
// number of digits in a uint32_t + NUL
|
||||
p += 11;
|
||||
*--p = 0;
|
||||
do {
|
||||
*--p = '0' + x % 10;
|
||||
x /= 10;
|
||||
} while (x);
|
||||
return p;
|
||||
}
|
||||
|
||||
int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf, size_t *size, char ***mem, size_t *nmem, struct group **res)
|
||||
{
|
||||
@ -10,7 +26,6 @@ int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf, size_t
|
||||
*res = 0;
|
||||
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
|
||||
|
||||
f = fopen("/etc/group", "rbe");
|
||||
if (!f) {
|
||||
rv = errno;
|
||||
@ -25,6 +40,129 @@ int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf, size_t
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (!*res && (rv == 0 || rv == ENOENT || rv == ENOTDIR)) {
|
||||
int32_t req = name ? GETGRBYNAME : GETGRBYGID;
|
||||
int32_t i;
|
||||
const char *key;
|
||||
int32_t groupbuf[GR_LEN] = {0};
|
||||
size_t len = 0;
|
||||
size_t grlist_len = 0;
|
||||
char gidbuf[11] = {0};
|
||||
int swap = 0;
|
||||
char *ptr;
|
||||
|
||||
if (name) {
|
||||
key = name;
|
||||
} else {
|
||||
if (gid < 0 || gid > UINT32_MAX) {
|
||||
rv = 0;
|
||||
goto done;
|
||||
}
|
||||
key = itoa(gidbuf, gid);
|
||||
}
|
||||
|
||||
f = __nscd_query(req, key, groupbuf, sizeof groupbuf, &swap);
|
||||
if (!f) { rv = errno; goto done; }
|
||||
if (f == (FILE*)-1) { rv = 0; goto done; }
|
||||
|
||||
if (!groupbuf[GRFOUND]) { rv = 0; goto cleanup_f; }
|
||||
|
||||
if (!groupbuf[GRNAMELEN] || !groupbuf[GRPASSWDLEN]) {
|
||||
rv = EIO;
|
||||
goto cleanup_f;
|
||||
}
|
||||
|
||||
if (groupbuf[GRNAMELEN] > SIZE_MAX - groupbuf[GRPASSWDLEN]) {
|
||||
rv = ENOMEM;
|
||||
goto cleanup_f;
|
||||
}
|
||||
len = groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN];
|
||||
|
||||
for (i = 0; i < groupbuf[GRMEMCNT]; i++) {
|
||||
uint32_t name_len;
|
||||
if (fread(&name_len, sizeof name_len, 1, f) < 1) {
|
||||
rv = ferror(f) ? errno : EIO;
|
||||
goto cleanup_f;
|
||||
}
|
||||
if (swap) {
|
||||
name_len = bswap_32(name_len);
|
||||
}
|
||||
if (name_len > SIZE_MAX - grlist_len
|
||||
|| name_len > SIZE_MAX - len) {
|
||||
rv = ENOMEM;
|
||||
goto cleanup_f;
|
||||
}
|
||||
len += name_len;
|
||||
grlist_len += name_len;
|
||||
}
|
||||
|
||||
if (len > *size || !*buf) {
|
||||
char *tmp = realloc(*buf, len);
|
||||
if (!tmp) {
|
||||
rv = errno;
|
||||
goto cleanup_f;
|
||||
}
|
||||
*buf = tmp;
|
||||
*size = len;
|
||||
}
|
||||
|
||||
if (!fread(*buf, len, 1, f)) {
|
||||
rv = ferror(f) ? errno : EIO;
|
||||
goto cleanup_f;
|
||||
}
|
||||
|
||||
if (groupbuf[GRMEMCNT] + 1 > *nmem) {
|
||||
if (groupbuf[GRMEMCNT] + 1 > SIZE_MAX/sizeof(char*)) {
|
||||
rv = ENOMEM;
|
||||
goto cleanup_f;
|
||||
}
|
||||
char **tmp = realloc(*mem, (groupbuf[GRMEMCNT]+1)*sizeof(char*));
|
||||
if (!tmp) {
|
||||
rv = errno;
|
||||
goto cleanup_f;
|
||||
}
|
||||
*mem = tmp;
|
||||
*nmem = groupbuf[GRMEMCNT] + 1;
|
||||
}
|
||||
|
||||
if (groupbuf[GRMEMCNT]) {
|
||||
mem[0][0] = *buf + groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN];
|
||||
for (ptr = mem[0][0], i = 0; ptr != mem[0][0]+grlist_len; ptr++)
|
||||
if (!*ptr) mem[0][++i] = ptr+1;
|
||||
mem[0][i] = 0;
|
||||
|
||||
if (i != groupbuf[GRMEMCNT]) {
|
||||
rv = EIO;
|
||||
goto cleanup_f;
|
||||
}
|
||||
} else {
|
||||
mem[0][0] = 0;
|
||||
}
|
||||
|
||||
gr->gr_name = *buf;
|
||||
gr->gr_passwd = gr->gr_name + groupbuf[GRNAMELEN];
|
||||
gr->gr_gid = groupbuf[GRGID];
|
||||
gr->gr_mem = *mem;
|
||||
|
||||
if (gr->gr_passwd[-1]
|
||||
|| gr->gr_passwd[groupbuf[GRPASSWDLEN]-1]) {
|
||||
rv = EIO;
|
||||
goto cleanup_f;
|
||||
}
|
||||
|
||||
if (name && strcmp(name, gr->gr_name)
|
||||
|| !name && gid != gr->gr_gid) {
|
||||
rv = EIO;
|
||||
goto cleanup_f;
|
||||
}
|
||||
|
||||
*res = gr;
|
||||
|
||||
cleanup_f:
|
||||
fclose(f);
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
pthread_setcancelstate(cs, 0);
|
||||
if (rv) errno = rv;
|
||||
|
@ -1,5 +1,21 @@
|
||||
#include "pwf.h"
|
||||
#include <pthread.h>
|
||||
#include <byteswap.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "pwf.h"
|
||||
#include "nscd.h"
|
||||
|
||||
static char *itoa(char *p, uint32_t x)
|
||||
{
|
||||
// number of digits in a uint32_t + NUL
|
||||
p += 11;
|
||||
*--p = 0;
|
||||
do {
|
||||
*--p = '0' + x % 10;
|
||||
x /= 10;
|
||||
} while (x);
|
||||
return p;
|
||||
}
|
||||
|
||||
int __getpw_a(const char *name, uid_t uid, struct passwd *pw, char **buf, size_t *size, struct passwd **res)
|
||||
{
|
||||
@ -24,6 +40,102 @@ int __getpw_a(const char *name, uid_t uid, struct passwd *pw, char **buf, size_t
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (!*res && (rv == 0 || rv == ENOENT || rv == ENOTDIR)) {
|
||||
int32_t req = name ? GETPWBYNAME : GETPWBYUID;
|
||||
const char *key;
|
||||
int32_t passwdbuf[PW_LEN] = {0};
|
||||
size_t len = 0;
|
||||
char uidbuf[11] = {0};
|
||||
|
||||
if (name) {
|
||||
key = name;
|
||||
} else {
|
||||
/* uid outside of this range can't be queried with the
|
||||
* nscd interface, but might happen if uid_t ever
|
||||
* happens to be a larger type (this is not true as of
|
||||
* now)
|
||||
*/
|
||||
if(uid < 0 || uid > UINT32_MAX) {
|
||||
rv = 0;
|
||||
goto done;
|
||||
}
|
||||
key = itoa(uidbuf, uid);
|
||||
}
|
||||
|
||||
f = __nscd_query(req, key, passwdbuf, sizeof passwdbuf, (int[]){0});
|
||||
if (!f) { rv = errno; goto done; }
|
||||
if (f == (FILE*)-1) { rv = 0; goto done; }
|
||||
|
||||
if(!passwdbuf[PWFOUND]) { rv = 0; goto cleanup_f; }
|
||||
|
||||
/* A zero length response from nscd is invalid. We ignore
|
||||
* invalid responses and just report an error, rather than
|
||||
* trying to do something with them.
|
||||
*/
|
||||
if (!passwdbuf[PWNAMELEN] || !passwdbuf[PWPASSWDLEN]
|
||||
|| !passwdbuf[PWGECOSLEN] || !passwdbuf[PWDIRLEN]
|
||||
|| !passwdbuf[PWSHELLLEN]) {
|
||||
rv = EIO;
|
||||
goto cleanup_f;
|
||||
}
|
||||
|
||||
if ((passwdbuf[PWNAMELEN]|passwdbuf[PWPASSWDLEN]
|
||||
|passwdbuf[PWGECOSLEN]|passwdbuf[PWDIRLEN]
|
||||
|passwdbuf[PWSHELLLEN]) >= SIZE_MAX/8) {
|
||||
rv = ENOMEM;
|
||||
goto cleanup_f;
|
||||
}
|
||||
|
||||
len = passwdbuf[PWNAMELEN] + passwdbuf[PWPASSWDLEN]
|
||||
+ passwdbuf[PWGECOSLEN] + passwdbuf[PWDIRLEN]
|
||||
+ passwdbuf[PWSHELLLEN];
|
||||
|
||||
if (len > *size || !*buf) {
|
||||
char *tmp = realloc(*buf, len);
|
||||
if (!tmp) {
|
||||
rv = errno;
|
||||
goto cleanup_f;
|
||||
}
|
||||
*buf = tmp;
|
||||
*size = len;
|
||||
}
|
||||
|
||||
if (!fread(*buf, len, 1, f)) {
|
||||
rv = ferror(f) ? errno : EIO;
|
||||
goto cleanup_f;
|
||||
}
|
||||
|
||||
pw->pw_name = *buf;
|
||||
pw->pw_passwd = pw->pw_name + passwdbuf[PWNAMELEN];
|
||||
pw->pw_gecos = pw->pw_passwd + passwdbuf[PWPASSWDLEN];
|
||||
pw->pw_dir = pw->pw_gecos + passwdbuf[PWGECOSLEN];
|
||||
pw->pw_shell = pw->pw_dir + passwdbuf[PWDIRLEN];
|
||||
pw->pw_uid = passwdbuf[PWUID];
|
||||
pw->pw_gid = passwdbuf[PWGID];
|
||||
|
||||
/* Don't assume that nscd made sure to null terminate strings.
|
||||
* It's supposed to, but malicious nscd should be ignored
|
||||
* rather than causing a crash.
|
||||
*/
|
||||
if (pw->pw_passwd[-1] || pw->pw_gecos[-1] || pw->pw_dir[-1]
|
||||
|| pw->pw_shell[passwdbuf[PWSHELLLEN]-1]) {
|
||||
rv = EIO;
|
||||
goto cleanup_f;
|
||||
}
|
||||
|
||||
if (name && strcmp(name, pw->pw_name)
|
||||
|| !name && uid != pw->pw_uid) {
|
||||
rv = EIO;
|
||||
goto cleanup_f;
|
||||
}
|
||||
|
||||
|
||||
*res = pw;
|
||||
cleanup_f:
|
||||
fclose(f);
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
pthread_setcancelstate(cs, 0);
|
||||
if (rv) errno = rv;
|
||||
|
38
src/passwd/nscd.h
Normal file
38
src/passwd/nscd.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef NSCD_H
|
||||
#define NSCD_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define NSCDVERSION 2
|
||||
#define GETPWBYNAME 0
|
||||
#define GETPWBYUID 1
|
||||
#define GETGRBYNAME 2
|
||||
#define GETGRBYGID 3
|
||||
|
||||
#define REQVERSION 0
|
||||
#define REQTYPE 1
|
||||
#define REQKEYLEN 2
|
||||
#define REQ_LEN 3
|
||||
|
||||
#define PWVERSION 0
|
||||
#define PWFOUND 1
|
||||
#define PWNAMELEN 2
|
||||
#define PWPASSWDLEN 3
|
||||
#define PWUID 4
|
||||
#define PWGID 5
|
||||
#define PWGECOSLEN 6
|
||||
#define PWDIRLEN 7
|
||||
#define PWSHELLLEN 8
|
||||
#define PW_LEN 9
|
||||
|
||||
#define GRVERSION 0
|
||||
#define GRFOUND 1
|
||||
#define GRNAMELEN 2
|
||||
#define GRPASSWDLEN 3
|
||||
#define GRGID 4
|
||||
#define GRMEMCNT 5
|
||||
#define GR_LEN 6
|
||||
|
||||
FILE *__nscd_query(int32_t req, const char *key, int32_t *buf, size_t len, int *swap);
|
||||
|
||||
#endif
|
100
src/passwd/nscd_query.c
Normal file
100
src/passwd/nscd_query.c
Normal file
@ -0,0 +1,100 @@
|
||||
#include <sys/socket.h>
|
||||
#include <byteswap.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "nscd.h"
|
||||
|
||||
static const struct {
|
||||
short sun_family;
|
||||
char sun_path[21];
|
||||
} addr = {
|
||||
AF_UNIX,
|
||||
"/var/run/nscd/socket"
|
||||
};
|
||||
|
||||
FILE *__nscd_query(int32_t req, const char *key, int32_t *buf, size_t len, int *swap)
|
||||
{
|
||||
size_t i;
|
||||
int fd;
|
||||
FILE *f = 0;
|
||||
int32_t req_buf[REQ_LEN] = {
|
||||
NSCDVERSION,
|
||||
req,
|
||||
strlen(key)+1
|
||||
};
|
||||
struct msghdr msg = {
|
||||
.msg_iov = (struct iovec[]){
|
||||
{&req_buf, sizeof(req_buf)},
|
||||
{(char*)key, strlen(key)+1}
|
||||
},
|
||||
.msg_iovlen = 2
|
||||
};
|
||||
|
||||
if (strlen(key) > INT32_MAX - 1) {
|
||||
return (FILE*)-1;
|
||||
}
|
||||
|
||||
*swap = 0;
|
||||
retry:
|
||||
|
||||
fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (fd < 0) return NULL;
|
||||
|
||||
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
/* If there isn't a running nscd we return -1 to indicate that
|
||||
* that is precisely what happened
|
||||
*/
|
||||
if (errno == EACCES || errno == ECONNREFUSED || errno == ENOENT) {
|
||||
close(fd);
|
||||
return (FILE *)-1;
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (sendmsg(fd, &msg, MSG_NOSIGNAL) < 0)
|
||||
goto error;
|
||||
|
||||
if(!(f = fdopen(fd, "r"))) goto error;
|
||||
|
||||
if (!fread(buf, len, 1, f)) {
|
||||
/* If the VERSION entry mismatches nscd will disconnect. The
|
||||
* most likely cause is that the endianness mismatched. So, we
|
||||
* byteswap and try once more. (if we already swapped, just
|
||||
* fail out)
|
||||
*/
|
||||
if (ferror(f)) goto error;
|
||||
if (!*swap) {
|
||||
fclose(f);
|
||||
for (i = 0; i < sizeof(req_buf)/sizeof(req_buf[0]); i++) {
|
||||
req_buf[i] = bswap_32(req_buf[i]);
|
||||
}
|
||||
*swap = 1;
|
||||
goto retry;
|
||||
} else {
|
||||
errno = EIO;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (*swap) {
|
||||
for (i = 0; i < len/sizeof(buf[0]); i++) {
|
||||
buf[i] = bswap_32(buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* The first entry in every nscd response is the version number. This
|
||||
* really shouldn't happen, and is evidence of some form of malformed
|
||||
* response.
|
||||
*/
|
||||
if(buf[0] != NSCDVERSION) {
|
||||
errno = EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
return f;
|
||||
error:
|
||||
if (f) fclose(f); else close(fd);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user