From 0edfa120eb49003f07d384e9de933b7d8f45d00d Mon Sep 17 00:00:00 2001 From: Michael Spang Date: Sat, 31 Jan 2009 01:46:30 -0500 Subject: [PATCH] Add op-adduser --- src/Makefile | 18 ++- src/ceo.proto | 23 ++++ src/op-adduser.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 src/ceo.proto create mode 100644 src/op-adduser.c diff --git a/src/Makefile b/src/Makefile index d5ac050..f1d94bb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -8,7 +8,7 @@ DESTDIR := PREFIX := /usr/local BIN_PROGS := addmember addclub zfsaddhomedir ceod -LIB_PROGS := ceoc +LIB_PROGS := ceoc op-adduser EXT_PROGS := config-test LIBCEO_OBJECTS := common.o addhomedir.o @@ -16,13 +16,16 @@ LIBCEO_LDFLAGS := LIBCEO_PROGS := addmember addclub LDAP_OBJECTS := ldap.o LDAP_LDFLAGS := -lldap -LDAP_PROGS := addmember addclub +LDAP_PROGS := addmember addclub op-adduser KRB5_OBJECTS := krb5.o kadm.o KRB5_LDFLAGS := $(shell krb5-config --libs krb5 kadm-client) -KRB5_PROGS := addmember addclub +KRB5_PROGS := addmember addclub op-adduser NET_OBJECTS := net.o gss.o ops.o NET_LDFLAGS := -lsctp $(shell krb5-config --libs gssapi) NET_PROGS := ceod ceoc +PROTO_OBJECTS := ceo.pb-c.o +PROTO_LDFLAGS := -lprotobuf-c +PROTO_PROGS := op-adduser CONFIG_OBJECTS := config.o parser.o CONFIG_LDFLAGS := CONFIG_PROGS := $(OLDCEO_PROGS) $(LDAP_PROGS) $(KRB5_PROGS) $(NET_PROGS) @@ -32,7 +35,12 @@ UTIL_PROGS := config-test zfsaddhomedir $(CONFIG_PROGS) all: $(BIN_PROGS) $(LIB_PROGS) $(EXT_PROGS) clean: - rm -f $(ALL_PROGS) $(EXT_PROGS) *.o + rm -f $(ALL_PROGS) $(EXT_PROGS) *.o ceo.pb-c.c ceo.pb-c.h + +op-adduser.o addmember.o addclub.o: ceo.pb-c.h + +ceo.pb-c.c ceo.pb-c.h: ceo.proto + protoc-c --c_out=. ceo.proto ceod: dmaster.o dslave.o $(CC) $(LDFLAGS) -o $@ $^ @@ -53,6 +61,8 @@ $(LDAP_PROGS): LDFLAGS += $(LDAP_LDFLAGS) $(LDAP_PROGS): $(LDAP_OBJECTS) $(KRB5_PROGS): LDFLAGS += $(KRB5_LDFLAGS) $(KRB5_PROGS): $(KRB5_OBJECTS) +$(PROTO_PROGS): LDFLAGS += $(PROTO_LDFLAGS) +$(PROTO_PROGS): $(PROTO_OBJECTS) $(CONFIG_PROGS): LDFLAGS += $(CONFIG_LDFLAGS) $(CONFIG_PROGS): $(CONFIG_OBJECTS) $(UTIL_PROGS): LDFLAGS += $(UTIL_LDFLAGS) diff --git a/src/ceo.proto b/src/ceo.proto new file mode 100644 index 0000000..386bc77 --- /dev/null +++ b/src/ceo.proto @@ -0,0 +1,23 @@ +package ceo; + +message StatusMessage { + required int32 status = 1; + required string message = 2; +} + +message AddUser { + enum Type { + MEMBER = 1; + CLUB = 2; + } + + required Type type = 1; + required string username = 2; + optional string password = 3; + optional string realname = 4; + optional string program = 5; +} + +message AddUserResponse { + repeated StatusMessage messages = 1; +} diff --git a/src/op-adduser.c b/src/op-adduser.c new file mode 100644 index 0000000..5e1990e --- /dev/null +++ b/src/op-adduser.c @@ -0,0 +1,283 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "net.h" +#include "ceo.pb-c.h" +#include "config.h" +#include "gss.h" +#include "krb5.h" +#include "ldap.h" +#include "kadm.h" +#include "daemon.h" +#include "strbuf.h" + +char *prog; + +static const int MAX_MESSAGES = 32; +static const int MAX_MESGSIZE = 512; + +char *user_types[] = { + [CEO__ADD_USER__TYPE__MEMBER] = "member", + [CEO__ADD_USER__TYPE__CLUB] = "club", +}; + +Ceo__AddUserResponse *response_create(void) { + Ceo__AddUserResponse *r = xmalloc(sizeof(Ceo__AddUserResponse)); + ceo__add_user_response__init(r); + r->n_messages = 0; + r->messages = xmalloc(MAX_MESSAGES * sizeof(Ceo__StatusMessage *)); + return r; +} + +int32_t response_message(Ceo__AddUserResponse *r, int32_t status, char *fmt, ...) { + va_list args; + Ceo__StatusMessage *statusmsg = xmalloc(sizeof(Ceo__StatusMessage)); + char *message = xmalloc(MAX_MESGSIZE); + + va_start(args, fmt); + vsnprintf(message, MAX_MESGSIZE, fmt, args); + va_end(args); + + ceo__status_message__init(statusmsg); + statusmsg->status = status; + statusmsg->message = message; + + if (r->n_messages >= MAX_MESSAGES) + fatal("too many messages"); + r->messages[r->n_messages++] = statusmsg; + + if (status) + error("%s", message); + else + notice("%s", message); + + return status; +} + +void response_delete(Ceo__AddUserResponse *r) { + int i; + + for (i = 0; i < r->n_messages; i++) { + free(r->messages[i]->message); + free(r->messages[i]); + } + free(r->messages); + free(r); +} + + +static int check_adduser(Ceo__AddUser *in, Ceo__AddUserResponse *out, char *client) { + int office = check_group(client, "office"); + int syscom = check_group(client, "syscom"); + + notice("adding uid=%s cn=%s by %s", in->username, in->realname, client); + + if (!office && !syscom) + return response_message(out, EPERM, "%s not authorized to create users", client); + + if (!in->username) + return response_message(out, EINVAL, "missing required argument: username"); + if (!in->realname) + return response_message(out, EINVAL, "missing required argument: realname"); + + if (in->type == CEO__ADD_USER__TYPE__MEMBER) { + if (!in->password) + return response_message(out, EINVAL, "missing required argument: password"); + } else if (in->type == CEO__ADD_USER__TYPE__CLUB) { + if (in->password) + return response_message(out, EINVAL, "club accounts cannot have passwords"); + if (in->program) + return response_message(out, EINVAL, "club accounts cannot have programs"); + } else { + return response_message(out, EINVAL, "invalid user type: %d", in->type); + } + + if (getpwnam(in->username) != NULL) + return response_message(out, EEXIST, "user %s already exists", in->username); + + if (getgrnam(in->username) != NULL) + return response_message(out, EEXIST, "group %s already exists", in->username); + + if (ceo_user_exists(in->username)) + return response_message(out, EEXIST, "user %s already exists in LDAP", in->username); + + if (ceo_group_exists(in->username)) + return response_message(out, EEXIST, "group %s already exists in LDAP", in->username); + + return 0; +} + +static void adduser_spam(Ceo__AddUser *in, Ceo__AddUserResponse *out, char *client, char *prog, int status) { + char *argv[] = { + notify_hook, prog, client, + in->username, in->realname, in->program ?: "", + status ? "failure" : "success", NULL + }; + + struct strbuf message = STRBUF_INIT; + for (int i = 0; i < out->n_messages; i++) + strbuf_addf(&message, "%s\n", out->messages[i]->message); + + spawnv_msg(notify_hook, argv, &message); + strbuf_release(&message); +} + +static int32_t addmember(Ceo__AddUser *in, Ceo__AddUserResponse *out) { + char homedir[1024]; + int user_stat, group_stat, krb_stat; + int id; + + if (snprintf(homedir, sizeof(homedir), "%s/%s", + member_home, in->username) >= sizeof(homedir)) + fatal("homedir overflow"); + + if ((id = ceo_new_uid(member_min_id, member_max_id)) <= 0) + fatal("no available uids in range [%ld, %ld]", member_min_id, member_max_id); + + if ((krb_stat = ceo_del_princ(in->username))) + return response_message(out, EEXIST, "unable to overwrite orphaned kerberos principal %s", in->username); + + if ((krb_stat = ceo_add_princ(in->username, in->password))) + return response_message(out, EKERB, "unable to create kerberos principal %s", in->username); + response_message(out, 0, "successfully created principal"); + + if ((user_stat = ceo_add_user(in->username, users_base, "member", in->realname, homedir, + member_shell, id, "program", in->program, NULL))) + return response_message(out, ELDAP, "unable to create ldap account %s", in->username); + response_message(out, 0, "successfully created ldap account"); + + /* errors that occur after this point are not fatal */ + + if ((group_stat = ceo_add_group(in->username, groups_base, id))) + response_message(out, ELDAP, "unable to create ldap group %s", in->username); + else + response_message(out, 0, "successfully created ldap group"); + + return krb_stat || user_stat || group_stat; +} + +static int32_t addclub(Ceo__AddUser *in, Ceo__AddUserResponse *out) { + char homedir[1024]; + int krb_stat, user_stat, group_stat, sudo_stat; + int id; + + if (snprintf(homedir, sizeof(homedir), "%s/%s", + club_home, in->username) >= sizeof(homedir)) + fatal("homedir overflow"); + + if ((id = ceo_new_uid(club_min_id, club_max_id)) <= 0) + fatal("no available uids in range [%ld, %ld]", club_min_id, club_max_id); + + if ((krb_stat = ceo_del_princ(in->username))) + return response_message(out, EKERB, "unable to clear principal %s", in->username); + + if ((user_stat = ceo_add_user(in->username, users_base, "club", in->realname, homedir, + club_shell, id, NULL))) + return response_message(out, ELDAP, "unable to create ldap account %s", in->username); + response_message(out, 0, "successfully created ldap account"); + + /* errors that occur after this point are not fatal */ + + if ((group_stat = ceo_add_group(in->username, groups_base, id))) + response_message(out, ELDAP, "unable to create ldap group %s", in->username); + else + response_message(out, 0, "successfully created ldap group"); + + if ((sudo_stat = ceo_add_group_sudo(in->username, sudo_base))) + response_message(out, ELDAP, "unable to create ldap sudoers %s", in->username); + else + response_message(out, 0, "successfully created ldap sudoers"); + + return user_stat || group_stat || sudo_stat; +} + +static int32_t adduser(Ceo__AddUser *in, Ceo__AddUserResponse *out, char *client) { + int32_t chk_stat, status; + char *prog; + + chk_stat = check_adduser(in, out, client); + if (chk_stat) + return chk_stat; + + if (in->type == CEO__ADD_USER__TYPE__MEMBER) { + status = addmember(in, out); + prog = "addmember"; + } else if (in->type == CEO__ADD_USER__TYPE__CLUB) { + status = addclub(in, out); + prog = "addclub"; + } else { + fatal("unknown user type %d", in->type); + } + + if (status) + response_message(out, 0, "there were failures, please contact systems committee"); + + adduser_spam(in, out, client, prog, status); + + return status; +} + +void cmd_adduser(void) { + Ceo__AddUser *in_proto; + Ceo__AddUserResponse *out_proto = response_create(); + struct strbuf in = STRBUF_INIT; + struct strbuf out = STRBUF_INIT; + + if (strbuf_read(&in, STDIN_FILENO, 0) < 0) + fatalpe("read"); + + in_proto = ceo__add_user__unpack(&protobuf_c_default_allocator, + in.len, (uint8_t *)in.buf); + if (!in_proto) + fatal("malformed add user message"); + + char *client = getenv("CEO_USER"); + if (!client) + fatal("environment variable CEO_USER is not set"); + + adduser(in_proto, out_proto, client); + + strbuf_grow(&out, ceo__add_user_response__get_packed_size(out_proto)); + strbuf_setlen(&out, ceo__add_user_response__pack(out_proto, (uint8_t *)out.buf)); + full_write(STDOUT_FILENO, out.buf, out.len); + + ceo__add_user__free_unpacked(in_proto, &protobuf_c_default_allocator); + response_delete(out_proto); + + strbuf_release(&in); + strbuf_release(&out); +} + +int main(int argc, char *argv[]) { + prog = basename(argv[0]); + init_log(prog, LOG_PID, LOG_AUTHPRIV); + + configure(); + + if (setenv("KRB5CCNAME", "MEMORY:adduser", 1)) + fatalpe("setenv"); + + ceo_krb5_init(); + ceo_ldap_init(); + ceo_kadm_init(); + + cmd_adduser(); + + ceo_kadm_cleanup(); + ceo_ldap_cleanup(); + ceo_krb5_cleanup(); + + return 0; +}