about summary refs log tree commit diff
path: root/src/passwd/nscd_query.c
diff options
context:
space:
mode:
authorJosiah Worcester <josiahw@gmail.com>2015-02-22 20:58:10 -0600
committerRich Felker <dalias@aerifal.cx>2015-02-23 01:02:14 -0500
commit34b423d2077a4c799d2089068d3bec91fb800256 (patch)
tree7b852fdc3d580b4d5dc12494812a5a5d11328f66 /src/passwd/nscd_query.c
parent0afef1aa24b784c86ae6121ca39e999824086c7c (diff)
downloadmusl-34b423d2077a4c799d2089068d3bec91fb800256.tar.gz
musl-34b423d2077a4c799d2089068d3bec91fb800256.tar.xz
musl-34b423d2077a4c799d2089068d3bec91fb800256.zip
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).
Diffstat (limited to 'src/passwd/nscd_query.c')
-rw-r--r--src/passwd/nscd_query.c100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/passwd/nscd_query.c b/src/passwd/nscd_query.c
new file mode 100644
index 00000000..f8d0fc13
--- /dev/null
+++ b/src/passwd/nscd_query.c
@@ -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;
+}