commit c35679afc52aea4a7af860b3008148a541cec6b5 from: Omar Polo date: Sat Dec 18 15:36:18 2021 UTC implement virtual and userdata tables for users handling The idea is to abstract over the users that kamid manages. The virtual table allows to use a custom userbase instead of the local UNIX users. The userdata tables maps (virtual) users to their home directory, which is the only directory tree exported by kamid as of now. Together, they allow a powerful and flexible management of the users and their data. The idea is somewhat stolen from smtpd(8). commit - 8c3973d8b52685b5fb439202ea0e648bc7a739d0 commit + c35679afc52aea4a7af860b3008148a541cec6b5 blob - aa91895bd924b2c661536d07bacecae8348eff90 blob + a41d2ae2bf9aa58be23d5cfd18ea610a5d5dc088 --- kamid.c +++ kamid.c @@ -258,41 +258,98 @@ auth_table_by_id(uint32_t id) return NULL; } +static inline struct table * +virtual_table_by_id(uint32_t id) +{ + struct kd_listen_conf *listen; + + STAILQ_FOREACH(listen, &main_conf->listen_head, entry) { + if (listen->id == id) + return listen->virtual_table; + } + + return NULL; +} + +static inline struct table * +userdata_table_by_id(uint32_t id) +{ + struct kd_listen_conf *listen; + + STAILQ_FOREACH(listen, &main_conf->listen_head, entry) { + if (listen->id == id) + return listen->userdata_table; + } + + return NULL; +} + static inline void do_auth_tls(struct imsg *imsg) { - char *username = NULL; - struct passwd *pw; - struct table *t; - struct kd_auth_req auth; - int p[2]; + char *username = NULL, *user = NULL, *home = NULL, *local_user; + struct passwd *pw; + struct table *auth, *virt, *userdata; + struct kd_auth_req kauth; + int p[2], free_home = 1; - if (sizeof(auth) != IMSG_DATA_SIZE(*imsg)) + if (sizeof(kauth) != IMSG_DATA_SIZE(*imsg)) fatal("wrong size for IMSG_AUTH_TLS: " "got %lu; want %lu", IMSG_DATA_SIZE(*imsg), - sizeof(auth)); - memcpy(&auth, imsg->data, sizeof(auth)); + sizeof(kauth)); + memcpy(&kauth, imsg->data, sizeof(kauth)); - if (memmem(auth.hash, sizeof(auth.hash), "", 1) == NULL) + if (memmem(kauth.hash, sizeof(kauth.hash), "", 1) == NULL) fatal("non NUL-terminated hash received"); - log_debug("tls id=%u hash=%s", auth.listen_id, auth.hash); + log_debug("tls id=%u hash=%s", kauth.listen_id, kauth.hash); - if ((t = auth_table_by_id(auth.listen_id)) == NULL) + if ((auth = auth_table_by_id(kauth.listen_id)) == NULL) fatal("request for invalid listener id %d", imsg->hdr.pid); - if (table_lookup(t, auth.hash, &username) == -1) { - log_warnx("login failed for hash %s", auth.hash); - goto err; - } - - log_debug("matched local user %s", username); + virt = virtual_table_by_id(kauth.listen_id); + userdata = userdata_table_by_id(kauth.listen_id); - if ((pw = getpwnam(username)) == NULL) { - log_warnx("getpwnam(%s) failed", username); + if (table_lookup(auth, kauth.hash, &username) == -1) { + log_warnx("login failed for hash %s", kauth.hash); goto err; + } + + if (virt != NULL && table_lookup(virt, username, &user) == -1) { + log_warnx("virtual lookup failed for user %s", username); + goto err; + } + + /* the local user */ + local_user = user != NULL ? user : username; + + if (user != NULL) + log_debug("virtual user %s matches local user %s", + username, user); + else + log_debug("matched local user %s", username); + + if (userdata != NULL && table_lookup(userdata, username, &home) + == -1) { + log_warnx("userdata lookup failed for user %s", username); + goto err; + } else { + if ((pw = getpwnam(local_user)) == NULL) { + log_warnx("getpwnam(%s) failed", local_user); + goto err; + } + + free_home = 0; + home = pw->pw_dir; } + if (user != NULL) + log_debug("matched home %s for virtual user %s", + home, username); + else + log_debug("matched home %s for local user %s", + home, username); + if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, PF_UNSPEC, p) == -1) fatal("socketpair"); @@ -300,15 +357,21 @@ do_auth_tls(struct imsg *imsg) start_child(PROC_CLIENTCONN, p[1], debug, verbose); main_imsg_compose_listener(IMSG_AUTH, p[0], imsg->hdr.peerid, - username, strlen(username)+1); + local_user, strlen(local_user)+1); main_imsg_compose_listener(IMSG_AUTH_DIR, -1, imsg->hdr.peerid, - pw->pw_dir, strlen(pw->pw_dir)+1); + home, strlen(home)+1); free(username); + free(user); + if (free_home) + free(home); return; err: free(username); + free(user); + if (free_home) + free(home); main_imsg_compose_listener(IMSG_AUTH, -1, imsg->hdr.peerid, NULL, 0); } blob - 2f2206f69d92bb6baa73c551c57b0f72caa96181 blob + fd14ab7c7c727a8d7d30270434c294902e08c6e2 --- kamid.conf.5 +++ kamid.conf.5 @@ -84,6 +84,15 @@ rule. Use the given authentication .Ar table to authorize the clients. +.It Ic userdata Pf < Ar table Ns > +Maps user +.Pq virtuals or not +to their exported tree. +By default the user home directory obtained with +.Xr getpwnam 3 +is used. +.It Ic virtual Pf < Ar table Ns > +Maps virtual users to local user. .El .It Ic pki Ar pkiname Ic cert Ar certfile Associate certificate file blob - 35ea35e670012b8b3c48b5f91506e57c7c9fe75a blob + 9fa99a3c7244b99f835976cae7bb1dc48e7cdfda --- kamid.h +++ kamid.h @@ -97,7 +97,16 @@ struct kd_listen_conf { int fd; char iface[LINE_MAX]; uint16_t port; + + /* certificate hash => (virtual) user */ struct table *auth_table; + + /* virtual user => local user */ + struct table *virtual_table; + + /* (virtual) user => export directory */ + struct table *userdata_table; + char pki[LINE_MAX]; struct event ev; struct tls *ctx; blob - aceb48834f148d3f19d218da8fbfc0774606dbec blob + 946f6669b45b632adfcbc3bd1920d2554b3ebc5e --- parse.y +++ parse.y @@ -118,6 +118,8 @@ typedef struct { %token ON %token PKI PORT %token TABLE TLS +%token USERDATA +%token VIRTUAL %token YES %token STRING @@ -297,6 +299,16 @@ listen_opt : ON STRING PORT NUMBER { if (listener->auth_table != NULL) yyerror("listen auth already defined"); listener->auth_table = $2; + } + | USERDATA tableref { + if (listener->userdata_table != NULL) + yyerror("userdata table already defined"); + listener->userdata_table = $2; + } + | VIRTUAL tableref { + if (listener->virtual_table != NULL) + yyerror("virtual table already defined"); + listener->virtual_table = $2; } ; @@ -345,6 +357,8 @@ lookup(char *s) {"port", PORT}, {"table", TABLE}, {"tls", TLS}, + {"userdata", USERDATA}, + {"virtual", VIRTUAL}, {"yes", YES}, }; const struct keywords *p;