freebsd-nq/sbin/idmapd/idmapd.c

419 lines
8.0 KiB
C
Raw Normal View History

/* $FreeBSD$ */
/* $Id: idmapd.c,v 1.5 2003/11/05 14:58:58 rees Exp $ */
/*
* copyright (c) 2003
* the regents of the university of michigan
* all rights reserved
*
* permission is granted to use, copy, create derivative works and redistribute
* this software and such derivative works for any purpose, so long as the name
* of the university of michigan is not used in any advertising or publicity
* pertaining to the use or distribution of this software without specific,
* written prior authorization. if the above copyright notice or any other
* identification of the university of michigan is included in any copy of any
* portion of this software, then the disclaimer below must also be included.
*
* this software is provided as is, without representation from the university
* of michigan as to its fitness for any purpose, and without warranty by the
* university of michigan of any kind, either express or implied, including
* without limitation the implied warranties of merchantability and fitness for
* a particular purpose. the regents of the university of michigan shall not be
* liable for any damages, including special, indirect, incidental, or
* consequential damages, with respect to any claim arising out of or in
* connection with the use of the software, even if it has been or is hereafter
* advised of the possibility of such damages.
*/
/* XXX ignores the domain of received names. */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <nfs4client/nfs4_dev.h>
#include <nfs4client/nfs4_idmap.h>
#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#define DEV_PATH "/dev/nfs4"
#define DOMAIN "@FreeBSD.org"
#define BADUSER "nobody"
#define BADGROUP "nogroup"
#define BADUID 65534
#define BADGID 65533
struct idmap_e {
struct nfs4dev_msg msg;
TAILQ_ENTRY(idmap_e) next;
};
int fd, verbose;
char *domain = DOMAIN;
TAILQ_HEAD(, idmap_e) upcall_q;
#define add_idmap_e(E) do { \
assert(E != NULL); \
TAILQ_INSERT_TAIL(&upcall_q, E, next); \
} while(0)
#define remove_idmap_e(E) do { \
assert(E != NULL && !TAILQ_EMPTY(&upcall_q)); \
E = TAILQ_FIRST(&upcall_q); \
TAILQ_REMOVE(&upcall_q, E, next); \
} while(0)
#define get_idmap_e(E) do { \
if ((E = (struct idmap_e *) malloc(sizeof(struct idmap_e))) == NULL) {\
fprintf(stderr, "get_idmap_e(): error in malloc\n");\
} } while(0)
#define put_idmap_e(E) free(E)
/* from marius */
int
validateascii(char *string, u_int32_t len)
{
int i;
for (i = 0; i < len; i++) {
if (string[i] == '\0')
break;
if (string[i] & 0x80)
return (-1);
}
if (string[i] != '\0')
return (-1);
return (i + 1);
}
char *
idmap_prune_domain(struct idmap_msg * m)
{
size_t i;
size_t len;
char * ret = NULL;
if (m == NULL)
return NULL;
len = m->id_namelen;
if (validateascii(m->id_name, len) < 0) {
fprintf(stderr, "msg has invalid ascii\n");
return NULL;
}
for (i=0; i < len && m->id_name[i] != '@' ; i++);
ret = (char *)malloc(i+1);
if (ret == NULL)
return NULL;
bcopy(m->id_name, ret, i);
ret[i] = '\0';
return ret;
}
int
idmap_add_domain(struct idmap_msg * m, char * name)
{
size_t len, nlen;
if (m == NULL || name == NULL)
return -1;
len = strlen(name);
nlen = len + strlen(domain);
if (nlen > IDMAP_MAXNAMELEN)
return -1;
bcopy(name, &m->id_name[0], len);
bcopy(domain, &m->id_name[len], strlen(domain));
m->id_name[nlen] = '\0';
m->id_namelen = nlen;
return 0;
}
int
idmap_name(struct idmap_msg * m, char *name)
{
if (m == NULL || name == NULL || m->id_namelen != 0)
return -1;
if (idmap_add_domain(m, name))
return -1;
return 0;
}
int
idmap_id(struct idmap_msg * m, ident_t id)
{
if (m == NULL || m->id_namelen == 0) {
fprintf(stderr, "idmap_id: bad msg\n");
return -1;
}
switch(m->id_type) {
case IDMAP_TYPE_UID:
m->id_id.uid = id.uid;
break;
case IDMAP_TYPE_GID:
m->id_id.gid = id.gid;
break;
default:
return -1;
break;
};
return 0;
}
int
idmap_service(struct idmap_e * e)
{
struct idmap_msg * m;
struct passwd * pwd;
struct group * grp;
ident_t id;
char * name;
if (e == NULL) {
fprintf(stderr, "bad entry\n");
return -1;
}
if (e->msg.msg_vers != NFS4DEV_VERSION) {
fprintf(stderr, "kernel/userland version mismatch! %d/%d\n",
e->msg.msg_vers, NFS4DEV_VERSION);
return -1;
}
if (e->msg.msg_type != NFS4DEV_TYPE_IDMAP) {
fprintf(stderr, "bad type!\n");
return -1;
}
if (e->msg.msg_len != sizeof(struct idmap_msg)) {
fprintf(stderr, "bad message length: %zu/%zu\n", e->msg.msg_len,
sizeof(struct idmap_msg));
return -1;
}
if (verbose)
printf("servicing msg xid: %x\n", e->msg.msg_xid);
m = (struct idmap_msg *)e->msg.msg_data;
if (m->id_namelen != 0 && m->id_namelen != strlen(m->id_name)) {
fprintf(stderr, "bad name length in idmap_msg\n");
return -1;
}
switch (m->id_type) {
case IDMAP_TYPE_UID:
if (m->id_namelen == 0) {
/* id to name */
pwd = getpwuid(m->id_id.uid);
if (pwd == NULL) {
fprintf(stderr, "unknown uid %d!\n",
(uint32_t)m->id_id.uid);
name = BADUSER;
} else
name = pwd->pw_name;
if (idmap_name(m, name))
return -1;
} else {
/* name to id */
name = idmap_prune_domain(m);
if (name == NULL)
return -1;
pwd = getpwnam(name);
if (pwd == NULL) {
fprintf(stderr, "unknown username %s!\n", name);
id.uid = (uid_t)BADUID;
} else
id.uid = pwd->pw_uid;
free(name);
if (idmap_id(m, id))
return -1;
}
break;
case IDMAP_TYPE_GID:
if (m->id_namelen == 0) {
/* id to name */
grp = getgrgid(m->id_id.gid);
if (grp == NULL) {
fprintf(stderr, "unknown gid %d!\n",
(uint32_t)m->id_id.gid);
name = BADGROUP;
} else
name = grp->gr_name;
if (idmap_name(m, name))
return -1;
} else {
/* name to id */
name = idmap_prune_domain(m);
if (name == NULL)
return -1;
grp = getgrnam(name);
if (grp == NULL) {
fprintf(stderr, "unknown groupname %s!\n", name);
id.gid = (gid_t)BADGID;
} else
id.gid = grp->gr_gid;
free(name);
if (idmap_id(m, id))
return -1;
}
break;
default:
fprintf(stderr, "bad idmap type: %d\n", m->id_type);
return -1;
break;
}
return 0;
}
int
main(int argc, char ** argv)
{
int error = 0;
struct idmap_e * entry;
fd_set read_fds, write_fds;
int maxfd;
int ret, ch;
while ((ch = getopt(argc, argv, "d:v")) != -1) {
switch (ch) {
case 'd':
domain = optarg;
break;
case 'v':
verbose = 1;
break;
default:
fprintf(stderr, "usage: %s [-v] [-d domain]\n", argv[0]);
exit(1);
break;
}
}
TAILQ_INIT(&upcall_q);
fd = open(DEV_PATH, O_RDWR, S_IRUSR | S_IWUSR);
if (fd < 0) {
perror(DEV_PATH);
exit(1);
}
if (!verbose)
daemon(0,0);
maxfd = fd;
for (;;) {
struct timeval timo = {1, 0};
do {
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_SET(fd, &read_fds);
FD_SET(fd, &write_fds);
ret = select(maxfd+1, &read_fds, &write_fds, NULL, &timo);
} while (ret < 0 && errno == EINTR);
if (ret <= 0) {
if (ret != 0)
perror("select");
continue;
}
if (FD_ISSET(fd, &read_fds)) {
for (;;) {
get_idmap_e(entry);
error = ioctl(fd, NFS4DEVIOCGET, &entry->msg);
if (error == -1) {
if (errno != EAGAIN)
perror("get ioctl:");
put_idmap_e(entry);
break;
}
switch (entry->msg.msg_type ) {
case NFS4DEV_TYPE_IDMAP:
if (idmap_service(entry))
entry->msg.msg_error = EIO;
break;
default:
fprintf(stderr, "unknown nfs4dev_msg type\n");
entry->msg.msg_error = EIO;
break;
}
add_idmap_e(entry);
}
}
if (FD_ISSET(fd, &write_fds)) {
while (!TAILQ_EMPTY(&upcall_q)) {
remove_idmap_e(entry);
error = ioctl(fd, NFS4DEVIOCPUT, &entry->msg);
if (error == -1) {
if (errno != EAGAIN)
perror("put ioctl");
break;
}
put_idmap_e(entry);
}
}
}
/* never reached */
exit(1);
}