diff --git a/Makefile b/Makefile index 8ff1dee..08ca231 100644 --- a/Makefile +++ b/Makefile @@ -24,25 +24,28 @@ INSTALL=install -v INSTALL_PROGRAM = $(INSTALL) INSTALL_DATA = $(INSTALL) -m 644 -CFLAGS=-O3 -Wall -Werror -#CFLAGS=-O0 -g -Wall +ifdef DEBUG + CFLAGS=-O0 -g -Wall -DDEBUG_MAX_CONNECTS=10 +else + CFLAGS=-O3 -Wall -Werror +endif all: mxshadowsrv libnss_mxshadow.so.2 test_server test_query_shadow -get_shadow_line.o: get_shadow_line.c get_shadow_line.h common.h - gcc $(CFLAGS) -c -fPIC -o get_shadow_line.o get_shadow_line.c - libnss_mxshadow.so.2: libnss_mxshadow.c get_shadow_line.c common.h - gcc $(CFLAGS) -shared -o libnss_mxshadow.so.2 -Wl,-soname,libnss_mxshadow.so.2,-unresolved-symbols=report-all -fPIC libnss_mxshadow.c get_shadow_line.c -l:libssl.a -l:libcrypto.a -lpthread -ldl + gcc $(CFLAGS) -shared -o libnss_mxshadow.so.2 -Wl,-soname,libnss_mxshadow.so.2,-unresolved-symbols=report-all -fPIC libnss_mxshadow.c -l:libssl.a -l:libcrypto.a -lpthread -ldl + +test_server: test_server.c get_shadow_line.c common.h + gcc $(CFLAGS) -o test_server test_server.c -l:libssl.a -l:libcrypto.a -lpthread -ldl -test_server: test_server.c get_shadow_line.c common.h get_shadow_line.h - gcc $(CFLAGS) -o test_server test_server.c get_shadow_line.c -l:libssl.a -l:libcrypto.a -lpthread -ldl +test_query_shadow: test_query_shadow.c get_shadow_line.c common.h + gcc $(CFLAGS) -o test_query_shadow test_query_shadow.c -l:libssl.a -l:libcrypto.a -lpthread -ldl mxshadowsrv: mxshadowsrv.c common.h gcc $(CFLAGS) -o mxshadowsrv mxshadowsrv.c -l:libssl.a -l:libcrypto.a -lpthread -ldl clean: - @rm *.o libnss_mxshadow.so.2 mxshadowsrv test_server test_query_shadow >/dev/null || true + @rm *.o libnss_mxshadow.so.2 mxshadowsrv test_server test_query_shadow 2>/dev/null || true install: all $(INSTALL) -D -t $(DESTDIR)$(usr_sbindir) mxshadowsrv diff --git a/common.h b/common.h index db41a92..4bdb577 100644 --- a/common.h +++ b/common.h @@ -1,11 +1,18 @@ +#ifndef _COMMON_H +#define _COMMON_H + #include #include #include #include -#include #define _cleanup_(x) __attribute__((cleanup(x))) +#ifndef COMMON_LOG +#include +#define COMMON_LOG(prio, msg, ...) (fprintf(stderr, msg "\n", ## __VA_ARGS__ )) +#endif + static void __attribute__((unused)) free_ssl_ctx(SSL_CTX **ctxp) { if (*ctxp) { SSL_CTX_free(*ctxp); @@ -37,8 +44,15 @@ static void __attribute__((unused)) free_string(char **ptr) { } static void __attribute__((unused)) psslerror(char *str) { - fprintf(stderr, "%s\n", str); - ERR_print_errors_fp(stderr); + COMMON_LOG(LOG_ERR, "%s:", str); + unsigned long ssl_err; + while ((ssl_err = ERR_get_error())) { + COMMON_LOG(LOG_ERR, "ssl error: %lud:%s:%s:%s", + ssl_err, + ERR_lib_error_string(ssl_err), + ERR_func_error_string(ssl_err), + ERR_reason_error_string(ssl_err)); + } } static int wait_rd_with_timeout(int fd, int timeout) { @@ -77,7 +91,8 @@ static int __attribute__((unused)) ssl_write_with_timeout(SSL *ssl, int fd, char if (status > 0) { if (status == datalen) return 0; - fprintf(stderr, "%s: unexpected partial write. requested %lud bytes, returned: %d\n", __func__, datalen, status); + COMMON_LOG(LOG_ERR, "%s: unexpected partial write. requested %lud bytes, returned: %d", __func__, datalen, status); + errno = EPIPE; return -1; } int ssl_error = SSL_get_error(ssl, status); @@ -88,14 +103,14 @@ static int __attribute__((unused)) ssl_write_with_timeout(SSL *ssl, int fd, char return -1; continue; case SSL_ERROR_SYSCALL: - perror(__func__); + COMMON_LOG(LOG_ERR, "%s: %m", __func__); return -1; case SSL_ERROR_SSL: - ERR_print_errors_fp(stderr); + psslerror(""); errno = EPROTO; return -1; default: - fprintf(stderr, "%s: %s unimplemented\n", __func__, ssl_err(ssl_error)); + COMMON_LOG(LOG_ERR, "%s: %s unimplemented", __func__, ssl_err(ssl_error)); errno = ENOSYS; return -1; } @@ -116,15 +131,16 @@ static int __attribute__((unused)) ssl_read_with_timeout(SSL *ssl, int fd, void continue; case SSL_ERROR_SYSCALL: if (errno==0) { - fprintf(stderr, "%s: unexpected EOF from peer\n", __func__); + COMMON_LOG(LOG_ERR, "%s: unexpected EOF from peer", __func__); + errno = ECONNABORTED; return -1; } - perror(__func__); + COMMON_LOG(LOG_ERR, "%s: %m", __func__); return -1; case SSL_ERROR_ZERO_RETURN: return 0; default: - fprintf(stderr, "%s: %s unimplemented\n", __func__, ssl_err(ssl_error)); + COMMON_LOG(LOG_ERR, "%s: %s unimplemented", __func__, ssl_err(ssl_error)); errno = ENOSYS; return -1; } @@ -144,16 +160,18 @@ static int __attribute__((unused)) ssl_accept_with_timeout(SSL *ssl, int fd, in return -1; continue; case SSL_ERROR_SYSCALL: - perror(__func__); + COMMON_LOG(LOG_ERR, "%s: %m", __func__); return -1; case SSL_ERROR_SSL: - ERR_print_errors_fp(stderr); + psslerror(""); errno = EPROTO; return -1; default: - fprintf(stderr, "%s: %s unimplemented\n", __func__, ssl_err(ssl_error)); + COMMON_LOG(LOG_ERR, "%s: %s unimplemented", __func__, ssl_err(ssl_error)); errno = ENOSYS; return -1; } } } + +#endif /* _COMMON_H */ diff --git a/get_shadow_line.c b/get_shadow_line.c index 25bd55f..8b80495 100644 --- a/get_shadow_line.c +++ b/get_shadow_line.c @@ -8,16 +8,16 @@ #include #include "common.h" -#include "get_shadow_line.h" -static char conf_filename[] = "/etc/mxshadow.conf"; +#define TIMEOUT 5000 static int read_config(struct sockaddr_in *addr) { + static const char conf_filename[] = "/etc/mxshadow.conf"; FILE *f _cleanup_(free_file) = NULL; char *line _cleanup_(free_string) = NULL; size_t n = 0; f = fopen(conf_filename, "r"); - if (f == NULL) { perror(conf_filename); return(-1); } + if (f == NULL) { COMMON_LOG(LOG_ERR, "%s: %m", conf_filename); return(-1); } char *server _cleanup_(free_string) = NULL; int port = -1; while (1) { @@ -25,7 +25,7 @@ static int read_config(struct sockaddr_in *addr) { if (status == -1) { if (feof(f)) break; - perror(conf_filename); + COMMON_LOG(LOG_ERR, "%s: %m", conf_filename); return -1; } char *comment = strchr(line, '#'); @@ -36,12 +36,15 @@ static int read_config(struct sockaddr_in *addr) { sscanf(line, "port = %u \n", &port); } if (server == NULL || port == -1) { - fprintf(stderr, "%s: does not set server and port\n", conf_filename); + COMMON_LOG(LOG_ERR, "%s: does not set server and port", conf_filename); + errno = EINVAL; return -1; } int status = inet_aton(server, &addr->sin_addr); if (status == 0) { - fprintf(stderr, "%s: not a invalid ip address: %s\n", conf_filename, server); + COMMON_LOG(LOG_ERR, "%s: not a invalid ip address: %s\n", conf_filename, server); + errno = EINVAL; + return -1; } addr->sin_port = htons(port); return 0; @@ -78,11 +81,11 @@ static int connect_with_timeout(int sockfd, struct sockaddr *addr, socklen_t add #define BUFLEN_SPWD (1024) -int get_shadow_line(char *user, char **line) { +static int get_shadow_line(char *user, char **line) { struct sockaddr_in sockaddr; bzero(&sockaddr, sizeof(sockaddr)); - sockaddr.sin_family =AF_INET; + sockaddr.sin_family = AF_INET; int status = read_config(&sockaddr); if (status == -1) return -1; @@ -94,10 +97,10 @@ int get_shadow_line(char *user, char **line) { if (SSL_CTX_load_verify_locations(ssl_ctx, "/etc/mxshadow.cert.pem", NULL) == 0) { psslerror("SSL_CTX_load_verify_locations"); return -1; } int sock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); - if (sock == 0) { perror("socket"); return -1; } + if (sock == 0) { COMMON_LOG(LOG_ERR, "socket: %m"); return -1; } - status = connect_with_timeout(sock, (struct sockaddr *)&sockaddr, sizeof(sockaddr), 1000); - if (status == -1) { perror("connect"); return -1; } + status = connect_with_timeout(sock, (struct sockaddr *)&sockaddr, sizeof(sockaddr), TIMEOUT); + if (status == -1) { COMMON_LOG(LOG_ERR, "connect: %m"); return -1; } SSL *ssl _cleanup_(free_ssl) = SSL_new(ssl_ctx); if (ssl == NULL) { psslerror("SSL_new"); return -1; } @@ -108,9 +111,9 @@ int get_shadow_line(char *user, char **line) { if (status == 0) { psslerror("SSL_set_fd"); return -1; } int len = strlen(user); - status = ssl_write_with_timeout(ssl, sock, user, len, 1000); + status = ssl_write_with_timeout(ssl, sock, user, len, TIMEOUT); if (status == -1) { - fprintf(stderr, "ssl_write_with_timeout failed\n"); + COMMON_LOG(LOG_ERR, "ssl_write_with_timeout failed"); return -1; } @@ -118,7 +121,7 @@ int get_shadow_line(char *user, char **line) { if (buffer == NULL) return -1; - len = ssl_read_with_timeout(ssl, sock, buffer, BUFLEN_SPWD, 1000); + len = ssl_read_with_timeout(ssl, sock, buffer, BUFLEN_SPWD, TIMEOUT); if (len<0) return -1; SSL_shutdown(ssl); diff --git a/get_shadow_line.h b/get_shadow_line.h deleted file mode 100644 index 3c0811d..0000000 --- a/get_shadow_line.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _GET_SHADOW_LINE_H -#define _GET_SHADOW_LINE_H - -int get_shadow_line(char *user, char **line); - -#endif /* _GET_SHADOW_LINE_H */ diff --git a/libnss_mxshadow.c b/libnss_mxshadow.c index 09a6e1d..6f5243e 100644 --- a/libnss_mxshadow.c +++ b/libnss_mxshadow.c @@ -1,8 +1,10 @@ #include #include #include +#include -#include "get_shadow_line.h" +#define COMMON_LOG(prio, msg, ...) (syslog(LOG_AUTH|prio, msg, ## __VA_ARGS__ )) +#include "get_shadow_line.c" #include "common.h" enum nss_status _nss_mxshadow_getspnam_r(const char *name, struct spwd *spwd, char *buffer, size_t buflen, int *errnop) { diff --git a/mxshadowsrv.c b/mxshadowsrv.c index 76b53c8..d8a5910 100644 --- a/mxshadowsrv.c +++ b/mxshadowsrv.c @@ -6,6 +6,11 @@ #include #include #include +#include +#include +#ifdef DEBUG_MAX_CONNECTS +#include +#endif #include "common.h" @@ -61,13 +66,29 @@ static void unmap_shadow(char *shadow_buf, struct stat *statbuf) { if (sts == -1) { perror("munmap"); exit(1); } } +#define MAX_THREADS 8 +#define TIMEOUT 30000 // client timeout in msec + +pthread_mutex_t shadow_mutex = PTHREAD_MUTEX_INITIALIZER ; +char *shadow_buf = NULL; // protected by shadow_mutex +struct stat statbuf; // protected by shadow_mutex +SSL_CTX *ssl_ctx; +sem_t free_worker; +#ifdef DEBUG_MAX_CONNECTS +static int debug_remaining_connects = DEBUG_MAX_CONNECTS; +#endif + static void validate_shadow(char **shadow_buf, char *filename, struct stat *statbuf) { + int status = pthread_mutex_lock(&shadow_mutex); + if (status != 0) { errno = status; perror("pthread_mutex_lock"); exit(1);} if (*shadow_buf == NULL) *shadow_buf = map_shadow(filename, statbuf); while(shadow_is_changed(filename, statbuf)) { unmap_shadow(*shadow_buf, statbuf); *shadow_buf = map_shadow(filename, statbuf); } + status = pthread_mutex_unlock(&shadow_mutex); + if (status != 0) { errno = status; perror("pthread_mutex_unlock"); exit(1);} } static int find_in_shadow(char *username, int username_len, char *shadow_buf, size_t shadow_len, char **lineptr) { @@ -130,6 +151,57 @@ static void die_usage(char *argv0) { exit(1); } +static void process_client(int socket) { + SSL *ssl _cleanup_(free_ssl) = SSL_new(ssl_ctx); + if (ssl == NULL) { psslerror("SSL_new"); return; } + SSL_set_fd(ssl, socket); + if (ssl_accept_with_timeout(ssl, socket, TIMEOUT) <= 0) { + perror("accept"); + return; + } + + char buf[64]; + int len = ssl_read_with_timeout(ssl, socket, buf, sizeof(buf), TIMEOUT); + if (len == 0) + return; + if (len < 0) { + perror("read"); + return; + } + if (len == sizeof(buf)) { + fprintf(stderr, "identifier to long\n"); + SSL_shutdown(ssl); + return; + } + + int status = pthread_mutex_lock(&shadow_mutex); + if (status != 0) { errno = status; perror("pthread_mutex_lock"); exit(1);} + + char *line; + int line_len; + line_len = find_in_shadow(buf, len, shadow_buf, statbuf.st_size, &line); + + status = pthread_mutex_unlock(&shadow_mutex); + if (status != 0) { errno = status; perror("pthread_mutex_unlock"); exit(1);} + + if (line_len) { + int status = ssl_write_with_timeout(ssl, socket, line, line_len, TIMEOUT); + if (status == -1) + perror("write"); + } + SSL_shutdown(ssl); +} + +static void *client_thread(void *arg) { + int socket = *((int *)arg); + process_client(socket); + close(socket); + free(arg); + int status = sem_post(&free_worker); + if (status != 0) { errno = status; perror("sem_post"); exit(1); } + return NULL; +} + int main(int argc, char **argv) { char *key_file = NULL; @@ -169,8 +241,9 @@ int main(int argc, char **argv) { die_usage(argv[0]); char *filename = argv[optind++]; - SSL_CTX *ssl_ctx _cleanup_(free_ssl_ctx) = SSL_CTX_new(TLS_server_method()); - if (ssl_ctx == NULL) { psslerror("SSL_CTX_new"); return 1; } + SSL_CTX *_ssl_ctx _cleanup_(free_ssl_ctx) = SSL_CTX_new(TLS_server_method()); + if (_ssl_ctx == NULL) { psslerror("SSL_CTX_new"); return 1; } + ssl_ctx = _ssl_ctx; if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) <= 0 ) { psslerror("SSL_CTX_use_PrivateKey_file"); return 1; } if (SSL_CTX_use_certificate_file(ssl_ctx, cert_file, SSL_FILETYPE_PEM) <= 0) { psslerror("SSL_CTX_use_certificate_file"); return 1; } @@ -192,44 +265,38 @@ int main(int argc, char **argv) { status = listen(listen_socket, 40); if (status == -1) { perror("listen"); exit(1); } - char *shadow_buf = NULL; - static struct stat statbuf; + status = sem_init(&free_worker, 0, MAX_THREADS); + if (status) { errno = status; perror("sem_init"); exit(1); } + while (1) { +#ifdef DEBUG_MAX_CONNECTS + if (debug_remaining_connects-- == 0) + break; +#endif int _cleanup_(free_fd) socket = accept4(listen_socket, NULL, NULL, SOCK_NONBLOCK); if (socket == -1 ) { perror("accept"); exit(1); } + status = sem_wait(&free_worker); + if (status == -1) { perror("sem_wait"); exit(1); } validate_shadow(&shadow_buf, filename, &statbuf); + pthread_t thread; + int *arg = malloc(sizeof *arg); + if (arg == NULL) { perror("malloc"); exit(1); } + *arg = socket; + status = pthread_create(&thread, NULL, client_thread, arg); + if (status != 0) { errno = status; perror("pthread_create"); exit(1); } + socket = -1; + status = pthread_detach(thread); + if (status != 0) { errno = status; perror("pthread_detach"); exit(1); } - SSL *ssl _cleanup_(free_ssl) = SSL_new(ssl_ctx); - if (ssl == NULL) { psslerror("SSL_new"); return 1; } - SSL_set_fd(ssl, socket); - if (ssl_accept_with_timeout(ssl, socket, 1000) <= 0) { - perror("accept"); - continue; - } - - char buf[64]; - int len = ssl_read_with_timeout(ssl, socket, buf, sizeof(buf), 1000); - if (len == 0) - continue; - if (len < 0) { - perror("read"); - continue; - } - if (len == sizeof(buf)) { - fprintf(stderr, "identifier to long\n"); - SSL_shutdown(ssl); - continue; - } - - char *line; - int line_len; - line_len = find_in_shadow(buf, len, shadow_buf, statbuf.st_size, &line); - if (line_len) { - int status = ssl_write_with_timeout(ssl, socket, line, line_len, 1000); - if (status == -1) - perror("write"); - } - SSL_shutdown(ssl); + } +#ifdef DEBUG_MAX_CONNECTS + for (int i=0; i #include -#include "get_shadow_line.h" +#include "get_shadow_line.c" int main(int argc, char **argv) { if (argc != 2) {