diff --git a/src/Makefile b/src/Makefile index 4f07fc3..d5ac050 100644 --- a/src/Makefile +++ b/src/Makefile @@ -8,6 +8,7 @@ DESTDIR := PREFIX := /usr/local BIN_PROGS := addmember addclub zfsaddhomedir ceod +LIB_PROGS := ceoc EXT_PROGS := config-test LIBCEO_OBJECTS := common.o addhomedir.o @@ -21,7 +22,7 @@ KRB5_LDFLAGS := $(shell krb5-config --libs krb5 kadm-client) KRB5_PROGS := addmember addclub NET_OBJECTS := net.o gss.o ops.o NET_LDFLAGS := -lsctp $(shell krb5-config --libs gssapi) -NET_PROGS := ceod +NET_PROGS := ceod ceoc CONFIG_OBJECTS := config.o parser.o CONFIG_LDFLAGS := CONFIG_PROGS := $(OLDCEO_PROGS) $(LDAP_PROGS) $(KRB5_PROGS) $(NET_PROGS) diff --git a/src/ceoc.c b/src/ceoc.c new file mode 100644 index 0000000..a4899f7 --- /dev/null +++ b/src/ceoc.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include + +#include "util.h" +#include "net.h" +#include "gss.h" +#include "ops.h" +#include "config.h" + +char *prog = NULL; + +static struct option opts[] = { + { NULL, 0, NULL, '\0' }, +}; + +static void usage() { + fprintf(stderr, "Usage: %s op\n", prog); + exit(2); +} + +static void send_gss_token(int sock, struct sockaddr *addr, socklen_t addrlen, gss_buffer_t token) { + OM_uint32 maj_stat, min_stat; + + if (sctp_sendmsg(sock, token->value, token->length, + addr, addrlen, MSG_AUTH, 0, 0, 0, 0) < 0) + fatalpe("sctp_sendmsg"); + + maj_stat = gss_release_buffer(&min_stat, token); + if (maj_stat != GSS_S_COMPLETE) + gss_fatal("gss_release_buffer", maj_stat, min_stat); +} + +static void client_gss_auth(int sock, struct sockaddr *addr, socklen_t addrlen) { + gss_buffer_desc incoming_tok, outgoing_tok; + struct sctp_meta msg_meta; + struct strbuf msg = STRBUF_INIT; + int complete; + + complete = initial_client_token(&outgoing_tok); + + for (;;) { + if (outgoing_tok.length) + send_gss_token(sock, addr, addrlen, &outgoing_tok); + else if (!complete) + fatal("no token to send during auth"); + + if (complete) + break; + + if (!receive_one_message(sock, &msg_meta, &msg)) + fatal("connection closed during auth"); + + if (msg_meta.sinfo.sinfo_ppid != MSG_AUTH) + fatal("unexpected message type 0x%x", msg_meta.sinfo.sinfo_ppid); + + incoming_tok.value = msg.buf; + incoming_tok.length = msg.len; + + complete = process_client_token(&incoming_tok, &outgoing_tok); + } + + strbuf_release(&msg); +} + +void run_remote(struct op *op, struct strbuf *in, struct strbuf *out) { + const char *hostname = op->hostname; + int sock = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); + struct sockaddr_in addr; + struct sctp_meta response_meta; + + if (!in->len) + fatal("no data to send"); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(9987); + addr.sin_addr = op->addr; + + struct sctp_event_subscribe events; + memset(&events, 0, sizeof(events)); + events.sctp_data_io_event = 1; + events.sctp_association_event = 1; + events.sctp_address_event = 1; + events.sctp_send_failure_event = 1; + events.sctp_peer_error_event = 1; + events.sctp_shutdown_event = 1; + events.sctp_partial_delivery_event = 1; + events.sctp_adaptation_layer_event = 1; + + if (setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events))) + fatalpe("setsockopt"); + + client_acquire_creds("ceod", hostname); + client_gss_auth(sock, (sa *)&addr, sizeof(addr)); + + if (sctp_sendmsg(sock, in->buf, in->len, (struct sockaddr *)&addr, + sizeof(addr), op->id, 0, 0, 0, 0) < 0) + fatalpe("sctp_sendmsg"); + + if (!receive_one_message(sock, &response_meta, out)) + fatal("no response received for op %s", op->name); + + if (response_meta.sinfo.sinfo_ppid != op->id) + fatal("wrong ppid from server: expected %d got %d", op->id, response_meta.sinfo.sinfo_ppid); + + if (sctp_sendmsg(sock, NULL, 0, (struct sockaddr *)&addr, + sizeof(addr), 0, SCTP_EOF, 0, 0 ,0) < 0) + fatalpe("sctp_sendmsg"); +} + +int client_main(char *op_name) { + struct op *op = find_op(op_name); + + if (!op) + fatal("no such op: %s", op_name); + + struct strbuf in = STRBUF_INIT; + struct strbuf out = STRBUF_INIT; + + if (strbuf_read(&in, STDIN_FILENO, 0) < 0) + fatalpe("read"); + + run_remote(op, &in, &out); + + if (strbuf_write(&out, STDOUT_FILENO) < 0) + fatalpe("write"); + + strbuf_release(&in); + strbuf_release(&out); + + return 0; +} + +int main(int argc, char *argv[]) { + int opt; + char *op; + + prog = basename(argv[0]); + init_log(prog, 0, LOG_USER); + + configure(); + setup_ops(); + + while ((opt = getopt_long(argc, argv, "", opts, NULL)) != -1) { + switch (opt) { + case '?': + usage(); + break; + default: + fatal("error parsing arguments"); + } + } + + if (argc - optind != 1) + usage(); + + op = argv[optind++]; + + return client_main(op); +}