Add ceod
This commit is contained in:
parent
7de577ab32
commit
ef6b18c7bb
10
src/Makefile
10
src/Makefile
|
@ -7,7 +7,7 @@ override CFLAGS += -std=gnu99 $(INCLUDES)
|
|||
DESTDIR :=
|
||||
PREFIX := /usr/local
|
||||
|
||||
BIN_PROGS := addmember addclub zfsaddhomedir
|
||||
BIN_PROGS := addmember addclub zfsaddhomedir ceod
|
||||
EXT_PROGS := config-test
|
||||
|
||||
LIBCEO_OBJECTS := common.o addhomedir.o
|
||||
|
@ -19,6 +19,9 @@ LDAP_PROGS := addmember addclub
|
|||
KRB5_OBJECTS := krb5.o kadm.o
|
||||
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
|
||||
CONFIG_OBJECTS := config.o parser.o
|
||||
CONFIG_LDFLAGS :=
|
||||
CONFIG_PROGS := $(OLDCEO_PROGS) $(LDAP_PROGS) $(KRB5_PROGS) $(NET_PROGS)
|
||||
|
@ -30,6 +33,9 @@ all: $(BIN_PROGS) $(LIB_PROGS) $(EXT_PROGS)
|
|||
clean:
|
||||
rm -f $(ALL_PROGS) $(EXT_PROGS) *.o
|
||||
|
||||
ceod: dmaster.o dslave.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
config-test: config-test.o parser.o
|
||||
|
||||
config.o: config.h config-vars.h
|
||||
|
@ -38,6 +44,8 @@ install:
|
|||
install -d $(DESTDIR)$(PREFIX)/bin
|
||||
install addmember addclub $(DESTDIR)$(PREFIX)/bin
|
||||
|
||||
$(NET_PROGS): LDFLAGS += $(NET_LDFLAGS)
|
||||
$(NET_PROGS): $(NET_OBJECTS)
|
||||
$(LIBCEO_PROGS): LDFLAGS += $(LIBCEO_LDFLAGS)
|
||||
$(LIBCEO_PROGS): $(LIBCEO_OBJECTS)
|
||||
$(LDAP_PROGS): LDFLAGS += $(LDAP_LDFLAGS)
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
/* dmain.c */
|
||||
extern int terminate;
|
||||
extern int fatal_signal;
|
||||
|
||||
/* dslave.c */
|
||||
void slave_main(int sock, struct sockaddr *addr);
|
||||
void setup_slave(void);
|
||||
|
||||
/* builtin-adduser.c */
|
||||
extern void builtin_adduser(struct sctp_meta *in, struct sctp_meta *out);
|
|
@ -0,0 +1,173 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <libgen.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <alloca.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "net.h"
|
||||
#include "config.h"
|
||||
#include "gss.h"
|
||||
#include "daemon.h"
|
||||
#include "ldap.h"
|
||||
#include "kadm.h"
|
||||
#include "krb5.h"
|
||||
#include "ops.h"
|
||||
|
||||
static struct option opts[] = {
|
||||
{ "detach", 0, NULL, 'd' },
|
||||
{ NULL, 0, NULL, '\0' },
|
||||
};
|
||||
|
||||
char *prog = NULL;
|
||||
|
||||
int terminate = 0;
|
||||
int fatal_signal;
|
||||
|
||||
static int detach = 0;
|
||||
|
||||
static void usage() {
|
||||
fprintf(stderr, "Usage: %s [--detach]\n", prog);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
static void signal_handler(int sig) {
|
||||
if (sig == SIGTERM || sig == SIGINT) {
|
||||
const char *s = (sig == SIGTERM) ? "terminated" : "interrupt";
|
||||
notice("shutting down (%s)", s);
|
||||
terminate = 1;
|
||||
fatal_signal = sig;
|
||||
signal(sig, SIG_DFL);
|
||||
} else if (sig == SIGSEGV) {
|
||||
error("segmentation fault");
|
||||
signal(sig, SIG_DFL);
|
||||
raise(sig);
|
||||
} else if (sig != SIGCHLD) {
|
||||
fatal("unhandled signal %d", sig);
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_signals(void) {
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler = signal_handler;
|
||||
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
sigaction(SIGTERM, &sa, NULL);
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
}
|
||||
|
||||
static void setup_daemon(void) {
|
||||
if (chdir("/"))
|
||||
fatalpe("chdir('/')");
|
||||
if (detach) {
|
||||
pid_t pid = fork();
|
||||
if (pid < 0)
|
||||
fatalpe("fork");
|
||||
if (pid)
|
||||
exit(0);
|
||||
if (setsid() < 0)
|
||||
fatalpe("setsid");
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_auth(void) {
|
||||
if (setenv("KRB5CCNAME", "MEMORY:ceod", 1))
|
||||
fatalpe("setenv");
|
||||
server_acquire_creds("ceod");
|
||||
}
|
||||
|
||||
static void accept_one_client(int server) {
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
memset(&addr, 0, addrlen);
|
||||
|
||||
int client = accept(server, (sa *)&addr, &addrlen);
|
||||
if (client < 0) {
|
||||
if (errno == EINTR)
|
||||
return;
|
||||
fatalpe("accept");
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
if (!pid) {
|
||||
close(server);
|
||||
slave_main(client, (sa *)&addr);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
close(client);
|
||||
}
|
||||
|
||||
static int master_main(void) {
|
||||
int sock;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(9987);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
|
||||
if (sock < 0)
|
||||
fatalpe("socket");
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)))
|
||||
fatalpe("bind");
|
||||
|
||||
if (listen(sock, 128))
|
||||
fatalpe("listen");
|
||||
|
||||
setup_daemon();
|
||||
setup_fqdn();
|
||||
setup_signals();
|
||||
setup_auth();
|
||||
setup_ops();
|
||||
|
||||
notice("now accepting connections");
|
||||
|
||||
while (!terminate)
|
||||
accept_one_client(sock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int opt;
|
||||
|
||||
prog = basename(argv[0]);
|
||||
init_log(prog, LOG_PID, LOG_DAEMON);
|
||||
|
||||
configure();
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "", opts, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
detach = 1;
|
||||
break;
|
||||
case '?':
|
||||
usage();
|
||||
break;
|
||||
default:
|
||||
fatal("error parsing arguments");
|
||||
}
|
||||
}
|
||||
|
||||
if (argc != optind)
|
||||
usage();
|
||||
|
||||
return master_main();
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <libgen.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <alloca.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "strbuf.h"
|
||||
#include "net.h"
|
||||
#include "config.h"
|
||||
#include "gss.h"
|
||||
#include "daemon.h"
|
||||
#include "ldap.h"
|
||||
#include "kadm.h"
|
||||
#include "krb5.h"
|
||||
#include "ops.h"
|
||||
|
||||
static void signal_handler(int sig) {
|
||||
if (sig == SIGSEGV) {
|
||||
error("segmentation fault");
|
||||
signal(sig, SIG_DFL);
|
||||
raise(sig);
|
||||
} else if (sig != SIGCHLD) {
|
||||
fatal("unhandled signal %d", sig);
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_slave_sigs(void) {
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler = signal_handler;
|
||||
|
||||
sigaction(SIGCHLD, &sa, NULL);
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
if (terminate)
|
||||
raise(fatal_signal);
|
||||
}
|
||||
|
||||
static void setup_client(int client, struct sockaddr *addr) {
|
||||
struct sctp_event_subscribe events;
|
||||
memset(&events, 0, sizeof(events));
|
||||
events.sctp_data_io_event = 1;
|
||||
events.sctp_address_event = 1;
|
||||
events.sctp_send_failure_event = 1;
|
||||
events.sctp_peer_error_event = 1;
|
||||
events.sctp_partial_delivery_event = 1;
|
||||
events.sctp_adaptation_layer_event = 1;
|
||||
|
||||
if (setsockopt(client, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events)))
|
||||
fatalpe("setsockopt(SCTP_EVENTS)");
|
||||
}
|
||||
|
||||
static void handle_auth_message(struct strbuf *in, struct strbuf *out) {
|
||||
gss_buffer_desc incoming_tok, outgoing_tok;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
|
||||
incoming_tok.value = in->buf;
|
||||
incoming_tok.length = in->len;
|
||||
|
||||
process_server_token(&incoming_tok, &outgoing_tok);
|
||||
|
||||
strbuf_add(out, outgoing_tok.value, outgoing_tok.length);
|
||||
|
||||
if (outgoing_tok.length) {
|
||||
maj_stat = gss_release_buffer(&min_stat, &outgoing_tok);
|
||||
if (maj_stat != GSS_S_COMPLETE)
|
||||
gss_fatal("gss_release_buffer", maj_stat, min_stat);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_op_message(uint32_t in_type, struct strbuf *in, struct strbuf *out) {
|
||||
struct op *op = get_local_op(in_type);
|
||||
char *envp[16];
|
||||
|
||||
debug("running op: %s", op->name);
|
||||
|
||||
if (!op->name)
|
||||
fatal("operation %x does not exist", in_type);
|
||||
|
||||
make_env(envp, "LANG", "C", "CEO_USER", client_username(),
|
||||
"CEO_CONFIG_DIR", config_dir, NULL);
|
||||
char *argv[] = { op->path, NULL, };
|
||||
|
||||
if (spawnvem(op->path, argv, envp, in, out, 0))
|
||||
fatal("child %s failed", op->path);
|
||||
|
||||
free_env(envp);
|
||||
}
|
||||
|
||||
static void handle_one_message(int sock, struct sctp_meta *in_meta, struct strbuf *in) {
|
||||
struct strbuf out = STRBUF_INIT;
|
||||
uint32_t ppid = in_meta->sinfo.sinfo_ppid;
|
||||
|
||||
if (ppid == MSG_AUTH)
|
||||
handle_auth_message(in, &out);
|
||||
else
|
||||
handle_op_message(ppid, in, &out);
|
||||
|
||||
if (out.len && sctp_sendmsg(sock, out.buf, out.len,
|
||||
(sa *)&in_meta->from, in_meta->fromlen, ppid,
|
||||
0, 0, 0, 0) < 0)
|
||||
fatalpe("sctp_sendmsg");
|
||||
|
||||
strbuf_release(&out);
|
||||
}
|
||||
|
||||
void slave_main(int sock, struct sockaddr *addr) {
|
||||
char addrstr[INET_ADDRSTRLEN];
|
||||
struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
|
||||
struct sctp_meta msg_meta;
|
||||
struct strbuf msg = STRBUF_INIT;
|
||||
|
||||
if (addr->sa_family != AF_INET)
|
||||
fatal("unsupported address family %d", addr->sa_family);
|
||||
|
||||
if (!inet_ntop(AF_INET, &addr_in->sin_addr, addrstr, sizeof(addrstr)))
|
||||
fatalpe("inet_ntop");
|
||||
|
||||
notice("accepted connection from %s", addrstr);
|
||||
|
||||
setup_slave_sigs();
|
||||
setup_client(sock, addr);
|
||||
|
||||
while (!terminate) {
|
||||
if (!receive_one_message(sock, &msg_meta, &msg))
|
||||
break;
|
||||
handle_one_message(sock, &msg_meta, &msg);
|
||||
}
|
||||
|
||||
notice("connection closed by peer %s", addrstr);
|
||||
|
||||
strbuf_release(&msg);
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <grp.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "gss.h"
|
||||
#include "net.h"
|
||||
#include "strbuf.h"
|
||||
|
||||
static gss_cred_id_t my_creds = GSS_C_NO_CREDENTIAL;
|
||||
static gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
|
||||
static gss_name_t peer_name = GSS_C_NO_NAME;
|
||||
static gss_name_t imported_service = GSS_C_NO_NAME;
|
||||
static gss_OID mech_type = GSS_C_NO_OID;
|
||||
static gss_buffer_desc peer_principal;
|
||||
static char *peer_username;
|
||||
static OM_uint32 ret_flags;
|
||||
static int complete;
|
||||
char service_name[128];
|
||||
|
||||
static void display_status(char *prefix, OM_uint32 code, int type) {
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
gss_buffer_desc msg;
|
||||
OM_uint32 msg_ctx = 0;
|
||||
|
||||
maj_stat = gss_display_status(&min_stat, code, type, GSS_C_NULL_OID,
|
||||
&msg_ctx, &msg);
|
||||
logmsg(LOG_ERR, "%s: %s", prefix, (char *)msg.value);
|
||||
gss_release_buffer(&min_stat, &msg);
|
||||
|
||||
while (msg_ctx) {
|
||||
maj_stat = gss_display_status(&min_stat, code, type, GSS_C_NULL_OID,
|
||||
&msg_ctx, &msg);
|
||||
logmsg(LOG_ERR, "additional: %s", (char *)msg.value);
|
||||
gss_release_buffer(&min_stat, &msg);
|
||||
}
|
||||
}
|
||||
|
||||
void gss_fatal(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat) {
|
||||
logmsg(LOG_ERR, "fatal: %s", msg);
|
||||
display_status("major", maj_stat, GSS_C_GSS_CODE);
|
||||
display_status("minor", min_stat, GSS_C_MECH_CODE);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void import_service(const char *service, const char *hostname) {
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
gss_buffer_desc buf_desc;
|
||||
|
||||
if (snprintf(service_name, sizeof(service_name),
|
||||
"%s@%s", service, hostname) >= sizeof(service_name))
|
||||
fatal("service name too long");
|
||||
|
||||
buf_desc.value = service_name;
|
||||
buf_desc.length = strlen(service_name);
|
||||
|
||||
maj_stat = gss_import_name(&min_stat, &buf_desc,
|
||||
GSS_C_NT_HOSTBASED_SERVICE, &imported_service);
|
||||
if (maj_stat != GSS_S_COMPLETE)
|
||||
gss_fatal("gss_import_name", maj_stat, min_stat);
|
||||
}
|
||||
|
||||
static void check_services(OM_uint32 flags) {
|
||||
debug("gss services: %sconf %sinteg %smutual %sreplay %ssequence",
|
||||
flags & GSS_C_CONF_FLAG ? "+" : "-",
|
||||
flags & GSS_C_INTEG_FLAG ? "+" : "-",
|
||||
flags & GSS_C_MUTUAL_FLAG ? "+" : "-",
|
||||
flags & GSS_C_REPLAY_FLAG ? "+" : "-",
|
||||
flags & GSS_C_SEQUENCE_FLAG ? "+" : "-");
|
||||
if (~flags & GSS_C_CONF_FLAG)
|
||||
fatal("confidentiality service required");
|
||||
if (~flags & GSS_C_INTEG_FLAG)
|
||||
fatal("integrity service required");
|
||||
if (~flags & GSS_C_MUTUAL_FLAG)
|
||||
fatal("mutual authentication required");
|
||||
}
|
||||
|
||||
void server_acquire_creds(const char *service) {
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
OM_uint32 time_rec;
|
||||
|
||||
if (!strlen(fqdn.buf))
|
||||
fatal("empty fqdn");
|
||||
|
||||
import_service(service, fqdn.buf);
|
||||
|
||||
notice("acquiring credentials for %s", service_name);
|
||||
|
||||
maj_stat = gss_acquire_cred(&min_stat, imported_service, GSS_C_INDEFINITE,
|
||||
GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &my_creds,
|
||||
NULL, &time_rec);
|
||||
if (maj_stat != GSS_S_COMPLETE)
|
||||
gss_fatal("gss_acquire_cred", maj_stat, min_stat);
|
||||
|
||||
if (time_rec != GSS_C_INDEFINITE)
|
||||
fatal("credentials valid for %d seconds (oops)", time_rec);
|
||||
}
|
||||
|
||||
void client_acquire_creds(const char *service, const char *hostname) {
|
||||
import_service(service, hostname);
|
||||
}
|
||||
|
||||
int process_server_token(gss_buffer_t incoming_tok, gss_buffer_t outgoing_tok) {
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
OM_uint32 time_rec;
|
||||
gss_OID name_type;
|
||||
|
||||
if (complete)
|
||||
fatal("unexpected %zd-byte token from peer", incoming_tok->length);
|
||||
|
||||
maj_stat = gss_accept_sec_context(&min_stat, &context_handle, my_creds,
|
||||
incoming_tok, GSS_C_NO_CHANNEL_BINDINGS, &peer_name, &mech_type,
|
||||
outgoing_tok, &ret_flags, &time_rec, NULL);
|
||||
if (maj_stat == GSS_S_COMPLETE) {
|
||||
check_services(ret_flags);
|
||||
|
||||
complete = 1;
|
||||
|
||||
maj_stat = gss_display_name(&min_stat, peer_name, &peer_principal, &name_type);
|
||||
if (maj_stat != GSS_S_COMPLETE)
|
||||
gss_fatal("gss_display_name", maj_stat, min_stat);
|
||||
|
||||
notice("client authenticated as %s", (char *)peer_principal.value);
|
||||
debug("context expires in %d seconds",time_rec);
|
||||
|
||||
} else if (maj_stat != GSS_S_CONTINUE_NEEDED) {
|
||||
gss_fatal("gss_accept_sec_context", maj_stat, min_stat);
|
||||
}
|
||||
|
||||
return complete;
|
||||
}
|
||||
|
||||
int process_client_token(gss_buffer_t incoming_tok, gss_buffer_t outgoing_tok) {
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
OM_uint32 time_rec;
|
||||
gss_OID_desc krb5 = *gss_mech_krb5;
|
||||
|
||||
if (complete)
|
||||
fatal("unexpected token from peer");
|
||||
|
||||
maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &context_handle,
|
||||
imported_service, &krb5, GSS_C_MUTUAL_FLAG |
|
||||
GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
|
||||
GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS,
|
||||
incoming_tok, NULL, outgoing_tok, &ret_flags,
|
||||
&time_rec);
|
||||
if (maj_stat == GSS_S_COMPLETE) {
|
||||
notice("server authenticated as %s", service_name);
|
||||
notice("context expires in %d seconds", time_rec);
|
||||
|
||||
check_services(ret_flags);
|
||||
|
||||
complete = 1;
|
||||
|
||||
} else if (maj_stat != GSS_S_CONTINUE_NEEDED) {
|
||||
gss_fatal("gss_init_sec_context", maj_stat, min_stat);
|
||||
}
|
||||
|
||||
return complete;
|
||||
}
|
||||
|
||||
int initial_client_token(gss_buffer_t outgoing_tok) {
|
||||
return process_client_token(GSS_C_NO_BUFFER, outgoing_tok);
|
||||
}
|
||||
|
||||
char *client_principal(void) {
|
||||
return complete ? (char *)peer_principal.value : NULL;
|
||||
}
|
||||
|
||||
char *client_username(void) {
|
||||
if (!peer_username) {
|
||||
char *princ = client_principal();
|
||||
if (princ) {
|
||||
peer_username = xstrdup(princ);
|
||||
char *c = strchr(peer_username, '@');
|
||||
if (c)
|
||||
*c = '\0';
|
||||
}
|
||||
}
|
||||
return peer_username;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#include <gssapi/gssapi.h>
|
||||
#include <gssapi/gssapi_krb5.h>
|
||||
|
||||
void server_acquire_creds(const char *service);
|
||||
void client_acquire_creds(const char *service, const char *hostname);
|
||||
void gss_fatal(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat);
|
||||
int process_server_token(gss_buffer_t incoming_tok, gss_buffer_t outgoing_tok);
|
||||
int process_client_token(gss_buffer_t incoming_tok, gss_buffer_t outgoing_tok);
|
||||
int initial_client_token(gss_buffer_t outgoing_tok);
|
||||
char *client_principal(void);
|
||||
char *client_username(void);
|
|
@ -0,0 +1,141 @@
|
|||
#include <stdio.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "net.h"
|
||||
#include "gss.h"
|
||||
#include "strbuf.h"
|
||||
|
||||
struct strbuf fqdn = STRBUF_INIT;
|
||||
|
||||
const size_t MAX_MSGLEN = 65536;
|
||||
const size_t MSG_BUFINC = 4096;
|
||||
|
||||
void setup_fqdn(void) {
|
||||
struct utsname uts;
|
||||
struct hostent *lo;
|
||||
|
||||
if (uname(&uts))
|
||||
fatalpe("uname");
|
||||
lo = gethostbyname(uts.nodename);
|
||||
if (!lo)
|
||||
fatalpe("gethostbyname");
|
||||
|
||||
strbuf_addstr(&fqdn, lo->h_name);
|
||||
}
|
||||
|
||||
static size_t recv_one_message(int sock, struct sctp_meta *msg_meta, struct strbuf *msg, int *notification) {
|
||||
size_t len = 0;
|
||||
int flags;
|
||||
int bytes;
|
||||
|
||||
strbuf_reset(msg);
|
||||
|
||||
do {
|
||||
msg_meta->fromlen = sizeof(msg_meta->from);
|
||||
|
||||
bytes = sctp_recvmsg(sock, msg->buf + len, strbuf_avail(msg) - len,
|
||||
(sa *)&msg_meta->from, &msg_meta->fromlen, &msg_meta->sinfo, &flags);
|
||||
|
||||
if (bytes < 0)
|
||||
fatalpe("sctp_recvmsg");
|
||||
if (!bytes)
|
||||
break;
|
||||
len += bytes;
|
||||
|
||||
if (msg->len > MAX_MSGLEN)
|
||||
fatal("oversized message received");
|
||||
if (strbuf_avail(msg) < MSG_BUFINC)
|
||||
strbuf_grow(msg, MSG_BUFINC);
|
||||
|
||||
} while (~flags & MSG_EOR);
|
||||
|
||||
if (!bytes && len)
|
||||
fatalpe("EOF in the middle of a message");
|
||||
|
||||
*notification = flags & MSG_NOTIFICATION;
|
||||
if (*notification)
|
||||
notification_dbg(msg->buf);
|
||||
|
||||
strbuf_setlen(msg, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
int receive_one_message(int sock, struct sctp_meta *msg_meta, struct strbuf *msg) {
|
||||
int notification = 0;
|
||||
|
||||
do {
|
||||
recv_one_message(sock, msg_meta, msg, ¬ification);
|
||||
} while (notification);
|
||||
|
||||
return msg->len > 0;
|
||||
}
|
||||
|
||||
void notification_dbg(char *notification) {
|
||||
union sctp_notification *sn = (union sctp_notification *) notification;
|
||||
char *extra;
|
||||
|
||||
switch (sn->sn_header.sn_type) {
|
||||
case SCTP_ASSOC_CHANGE:
|
||||
extra = "unknown state";
|
||||
switch (sn->sn_assoc_change.sac_state) {
|
||||
case SCTP_COMM_UP: extra = "established"; break;
|
||||
case SCTP_COMM_LOST: extra = "lost"; break;
|
||||
case SCTP_RESTART: extra = "restarted"; break;
|
||||
case SCTP_SHUTDOWN_COMP: extra = "completed shutdown"; break;
|
||||
case SCTP_CANT_STR_ASSOC: extra = "cannot start"; break;
|
||||
}
|
||||
debug("association changed: association 0x%x %s", sn->sn_assoc_change.sac_assoc_id, extra);
|
||||
break;
|
||||
case SCTP_PEER_ADDR_CHANGE:
|
||||
extra = "unknown state";
|
||||
switch (sn->sn_paddr_change.spc_state) {
|
||||
case SCTP_ADDR_AVAILABLE: extra = "unavailable"; break;
|
||||
case SCTP_ADDR_UNREACHABLE: extra = "unreachable"; break;
|
||||
case SCTP_ADDR_REMOVED: extra = "removed"; break;
|
||||
case SCTP_ADDR_ADDED: extra = "added"; break;
|
||||
case SCTP_ADDR_MADE_PRIM: extra = "made primary"; break;
|
||||
#ifdef SCTP_ADDR_CONFIRMED
|
||||
case SCTP_ADDR_CONFIRMED: extra = "confirmed"; break;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct sockaddr_in *sa = (struct sockaddr_in *) &sn->sn_paddr_change.spc_aaddr;
|
||||
char addr[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &sa->sin_addr, addr, sizeof(addr));
|
||||
debug("peer address change: remote address %s %s", addr, extra);
|
||||
break;
|
||||
case SCTP_REMOTE_ERROR:
|
||||
debug("remote error: association=0x%x error=0x%x",
|
||||
sn->sn_remote_error.sre_assoc_id,
|
||||
sn->sn_remote_error.sre_error);
|
||||
break;
|
||||
case SCTP_SEND_FAILED:
|
||||
debug("send failed: association=0x%x error=0x%x",
|
||||
sn->sn_send_failed.ssf_assoc_id,
|
||||
sn->sn_send_failed.ssf_error);
|
||||
break;
|
||||
case SCTP_ADAPTATION_INDICATION:
|
||||
debug("adaptation indication: 0x%x",
|
||||
sn->sn_adaptation_event.sai_adaptation_ind);
|
||||
break;
|
||||
case SCTP_PARTIAL_DELIVERY_EVENT:
|
||||
extra = "unknown indication";
|
||||
switch (sn->sn_pdapi_event.pdapi_indication) {
|
||||
case SCTP_PARTIAL_DELIVERY_ABORTED:
|
||||
extra = "partial delivery aborted";
|
||||
break;
|
||||
}
|
||||
debug("partial delivery event: %s", extra);
|
||||
break;
|
||||
case SCTP_SHUTDOWN_EVENT:
|
||||
debug("association 0x%x was shut down",
|
||||
sn->sn_shutdown_event.sse_assoc_id);
|
||||
break;
|
||||
default:
|
||||
debug("unknown sctp notification type 0x%x\n",
|
||||
sn->sn_header.sn_type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/sctp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <gssapi/gssapi.h>
|
||||
|
||||
#if ! defined(SCTP_ADDR_CONFIRMED) && defined(__linux__)
|
||||
#define SCTP_ADDR_CONFIRMED 5
|
||||
#endif
|
||||
|
||||
void notification_dbg(char *);
|
||||
|
||||
#ifdef SCTP_ADAPTION_LAYER
|
||||
#define sctp_adaptation_layer_event sctp_adaption_layer_event
|
||||
#define sn_adaptation_event sn_adaption_event
|
||||
#define sai_adaptation_ind sai_adaption_ind
|
||||
#define SCTP_ADAPTATION_INDICATION SCTP_ADAPTION_INDICATION
|
||||
#endif
|
||||
|
||||
typedef struct sockaddr sa;
|
||||
|
||||
extern struct strbuf fqdn;
|
||||
extern void setup_fqdn(void);
|
||||
|
||||
struct sctp_meta {
|
||||
struct sockaddr_storage from;
|
||||
socklen_t fromlen;
|
||||
struct sctp_sndrcvinfo sinfo;
|
||||
};
|
||||
|
||||
enum {
|
||||
MSG_AUTH = 0x8000000,
|
||||
MSG_EXPLODE = 0x8000001,
|
||||
};
|
||||
|
||||
#ifdef MSG_ABORT
|
||||
#define SCTP_ABORT MSG_ABORT
|
||||
#define SCTP_EOF MSG_EOF
|
||||
#endif
|
||||
|
||||
#define EKERB -2
|
||||
#define ELDAP -3
|
||||
|
||||
int receive_one_message(int sock, struct sctp_meta *msg_meta, struct strbuf *msg);
|
|
@ -0,0 +1,110 @@
|
|||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include "strbuf.h"
|
||||
#include "ops.h"
|
||||
#include "net.h"
|
||||
#include "util.h"
|
||||
#include "config.h"
|
||||
|
||||
static struct op *ops;
|
||||
|
||||
static const char *default_op_dir = "/usr/lib/ceod";
|
||||
static const char *op_dir;
|
||||
|
||||
static void add_op(char *host, char *name, uint32_t id) {
|
||||
struct op *new = xmalloc(sizeof(struct op));
|
||||
errno = 0;
|
||||
new->next = ops;
|
||||
new->name = xstrdup(name);
|
||||
new->id = id;
|
||||
new->path = NULL;
|
||||
|
||||
struct hostent *hostent = gethostbyname(host);
|
||||
if (!hostent)
|
||||
badconf("cannot add op %s: %s: %s", name, host, hstrerror(h_errno));
|
||||
new->hostname = strdup(hostent->h_name);
|
||||
new->local = !strcmp(fqdn.buf, hostent->h_name);
|
||||
new->addr = *(struct in_addr *)hostent->h_addr_list[0];
|
||||
|
||||
if (new->local) {
|
||||
new->path = xmalloc(strlen(op_dir) + strlen("/op-") + strlen(name) + 1);
|
||||
sprintf(new->path, "%s/op-%s", op_dir, name);
|
||||
if (access(new->path, X_OK))
|
||||
fatalpe("cannot add op: %s: %s", name, new->path);
|
||||
}
|
||||
|
||||
ops = new;
|
||||
debug("added op %s (%s%s)", new->name, new->local ? "" : "on ",
|
||||
new->local ? "local" : host);
|
||||
}
|
||||
|
||||
struct op *get_local_op(uint32_t id) {
|
||||
for (struct op *op = ops; op; op = op->next) {
|
||||
if (op->local && op->id == id)
|
||||
return op;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct op *find_op(const char *name) {
|
||||
for (struct op *op = ops; op; op = op->next) {
|
||||
if (!strcmp(name, op->name))
|
||||
return op;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void setup_ops(void) {
|
||||
char op_config_dir[1024];
|
||||
DIR *dp;
|
||||
struct dirent *de;
|
||||
struct strbuf line = STRBUF_INIT;
|
||||
unsigned lineno = 0;
|
||||
unsigned op_count = 0;
|
||||
|
||||
op_dir = getenv("CEO_LIB_DIR") ?: default_op_dir;
|
||||
|
||||
if (snprintf(op_config_dir, sizeof(op_config_dir), "%s/%s", config_dir, "ops.d") >= sizeof(op_config_dir))
|
||||
fatal("ops dir path too long");
|
||||
|
||||
dp = opendir(op_config_dir);
|
||||
if (!dp)
|
||||
fatalpe("opendir: %s", op_config_dir);
|
||||
|
||||
while ((de = readdir(dp))) {
|
||||
FILE *fp = fopenat(dp, de->d_name, O_RDONLY);
|
||||
if (!fp)
|
||||
warnpe("open: %s/%s", op_config_dir, de->d_name);
|
||||
while (strbuf_getline(&line, fp, '\n') != EOF) {
|
||||
lineno++;
|
||||
strbuf_trim(&line);
|
||||
|
||||
if (!line.len || line.buf[0] == '#')
|
||||
continue;
|
||||
|
||||
struct strbuf **words = strbuf_splitws(&line);
|
||||
|
||||
if (strbuf_list_len(words) != 3)
|
||||
badconf("%s/%s: expected three words on line %d", op_config_dir, de->d_name, lineno);
|
||||
|
||||
errno = 0;
|
||||
char *end;
|
||||
int id = strtol(words[2]->buf, &end, 0);
|
||||
if (errno || *end)
|
||||
badconf("%s/%s: invalid id '%s' on line %d", op_config_dir, de->d_name, words[2]->buf, lineno);
|
||||
|
||||
add_op(words[0]->buf, words[1]->buf, id);
|
||||
op_count++;
|
||||
|
||||
strbuf_list_free(words);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
strbuf_release(&line);
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
struct op {
|
||||
char *name;
|
||||
uint32_t id;
|
||||
int local;
|
||||
char *hostname;
|
||||
char *path;
|
||||
struct in_addr addr;
|
||||
struct op *next;
|
||||
};
|
||||
|
||||
void setup_ops(void);
|
||||
struct op *find_op(const char *name);
|
||||
struct op *get_local_op(uint32_t id);
|
127
src/util.c
127
src/util.c
|
@ -1,10 +1,13 @@
|
|||
#define _ATFILE_SOURCE
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <fcntl.h>
|
||||
#include <syslog.h>
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "strbuf.h"
|
||||
|
@ -148,3 +151,127 @@ int spawnv(const char *path, char *const argv[]) {
|
|||
exit(execv(path, argv));
|
||||
return status;
|
||||
}
|
||||
|
||||
void full_write(int fd, const void *buf, size_t count) {
|
||||
ssize_t total = 0;
|
||||
|
||||
while (total < count) {
|
||||
ssize_t wcount = write(fd, (char *)buf + total, count - total);
|
||||
if (wcount < 0)
|
||||
fatalpe("write");
|
||||
total += wcount;
|
||||
}
|
||||
}
|
||||
|
||||
int spawnvem(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr) {
|
||||
int pid, wpid, status;
|
||||
int tochild[2];
|
||||
int fmchild[2];
|
||||
|
||||
if (pipe(tochild))
|
||||
fatalpe("pipe");
|
||||
if (pipe(fmchild))
|
||||
fatalpe("pipe");
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
fatalpe("fork");
|
||||
if (!pid) {
|
||||
dup2(tochild[0], STDIN_FILENO);
|
||||
dup2(fmchild[1], STDOUT_FILENO);
|
||||
if (cap_stderr)
|
||||
dup2(STDOUT_FILENO, STDERR_FILENO);
|
||||
close(tochild[0]);
|
||||
close(tochild[1]);
|
||||
close(fmchild[0]);
|
||||
close(fmchild[1]);
|
||||
execve(path, argv, envp);
|
||||
fatalpe("execve");
|
||||
} else {
|
||||
close(tochild[0]);
|
||||
close(fmchild[1]);
|
||||
full_write(tochild[1], output->buf, output->len);
|
||||
close(tochild[1]);
|
||||
|
||||
if (input)
|
||||
strbuf_read(input, fmchild[0], 0);
|
||||
close(fmchild[0]);
|
||||
}
|
||||
|
||||
wpid = waitpid(pid, &status, 0);
|
||||
if (wpid < 0)
|
||||
fatalpe("waitpid");
|
||||
else if (wpid != pid)
|
||||
fatal("waitpid is broken");
|
||||
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status))
|
||||
notice("child %s exited with status %d", path, WEXITSTATUS(status));
|
||||
else if (WIFSIGNALED(status))
|
||||
notice("child %s killed by signal %d", path, WTERMSIG(status));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int spawnv_msg(const char *path, char *const *argv, const struct strbuf *output) {
|
||||
return spawnvem(path, argv, environ, output, NULL, 0);
|
||||
}
|
||||
|
||||
int check_group(char *username, char *group) {
|
||||
struct group *grp = getgrnam(group);
|
||||
char **members;
|
||||
|
||||
if (grp)
|
||||
for (members = grp->gr_mem; *members; members++)
|
||||
if (!strcmp(username, *members))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE *fopenat(DIR *d, const char *path, int flags) {
|
||||
int dfd = dirfd(d);
|
||||
if (dfd < 0)
|
||||
return NULL;
|
||||
int fd = openat(dfd, path, flags);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
return fdopen(fd, flags & O_RDWR ? "r+" :
|
||||
flags & O_WRONLY ? "w" :
|
||||
"r");
|
||||
}
|
||||
|
||||
void make_env(char **envp, ...) {
|
||||
const size_t len = 4096;
|
||||
size_t used = 0;
|
||||
int args = 0;
|
||||
char *buf = xmalloc(len);
|
||||
va_list ap;
|
||||
va_start(ap, envp);
|
||||
char *name, *val;
|
||||
|
||||
while ((name = va_arg(ap, char *))) {
|
||||
val = va_arg(ap, char *);
|
||||
if (!val)
|
||||
continue;
|
||||
int n = snprintf(buf + used, len - used, "%s=%s", name, val);
|
||||
if (n < 0)
|
||||
fatalpe("snprintf");
|
||||
if (n >= len - used)
|
||||
fatal("environment too big");
|
||||
|
||||
envp[args++] = buf + used;
|
||||
used += n + 1;
|
||||
}
|
||||
|
||||
if (!args)
|
||||
free(buf);
|
||||
|
||||
envp[args] = NULL;
|
||||
}
|
||||
|
||||
void free_env(char **envp) {
|
||||
free(*envp);
|
||||
}
|
||||
|
|
17
src/util.h
17
src/util.h
|
@ -2,8 +2,14 @@
|
|||
#define CEO_UTIL_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "strbuf.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define NORETURN __attribute__((__noreturn__))
|
||||
|
@ -17,8 +23,18 @@
|
|||
#define LOG_AUTHPRIV LOG_AUTH
|
||||
#endif
|
||||
|
||||
extern char **environ;
|
||||
|
||||
int spawnv(const char *path, char *const *argv);
|
||||
int spawnv_msg(const char *path, char *const *argv, const struct strbuf *output);
|
||||
int spawnvem(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr);
|
||||
void full_write(int fd, const void *buf, size_t count);
|
||||
ssize_t full_read(int fd, void *buf, size_t len);
|
||||
FILE *fopenat(DIR *d, const char *path, int flags);
|
||||
void make_env(char **envp, ...);
|
||||
void free_env(char **envp);
|
||||
void init_log(const char *ident, int option, int facility);
|
||||
int check_group(char *username, char *group);
|
||||
|
||||
PRINTF_LIKE(0) NORETURN void fatal(const char *, ...);
|
||||
PRINTF_LIKE(0) NORETURN void fatalpe(const char *, ...);
|
||||
|
@ -59,7 +75,6 @@ static inline void *xcalloc(size_t nmemb, size_t size) {
|
|||
return alloc;
|
||||
}
|
||||
|
||||
|
||||
static inline char *xstrdup(const char *s) {
|
||||
char *dup = strdup(s);
|
||||
|
||||
|
|
Loading…
Reference in New Issue