add auth mode protocol
This commit is contained in:
parent
3899af7b10
commit
12a71b794d
13
Makefile
13
Makefile
|
@ -1,9 +1,12 @@
|
|||
flags = -Wall -D_GNU_SOURCE -Iinclude
|
||||
lib-flags = $(flags) -fPIC -shared
|
||||
obj-flags = $(flags) -c
|
||||
exe-flags = $(flags) -ljwt
|
||||
# the --no-as-needed flag is passed to the linker
|
||||
# if I was building this correctly it wouldn't be necessary
|
||||
# but I'm not going to figure out how to do it right now (TODO: figure out)
|
||||
exe-flags = $(flags) -Wl,--no-as-needed -ljwt
|
||||
exe := bin/localserv
|
||||
src := $(wildcard src/*.c)
|
||||
src := $(wildcard src/*.c) $(wildcard src/**/*.c)
|
||||
obj := $(src:src/%.c=bin/%.o)
|
||||
lib-src := $(wildcard lib/*.c)
|
||||
lib-obj := $(lib-src:lib/%.c=bin/%.o)
|
||||
|
@ -21,21 +24,21 @@ server-lib client-lib: %-lib : bin/%.so
|
|||
$(exe): $(lib-obj) $(obj)
|
||||
@mkdir -p $(@D)
|
||||
@printf "Compiling executable $@ ...\n"
|
||||
gcc $(exe-flags) $^ -o $@
|
||||
@gcc $(exe-flags) $^ -o $@
|
||||
@printf "Done.\n"
|
||||
|
||||
$(lib-obj) : bin/%.o : include/%.h
|
||||
$(lib-obj) : bin/%.o : lib/%.c
|
||||
@mkdir -p $(@D)
|
||||
@printf "Compiling object for $< ...\n"
|
||||
gcc $(obj-flags) $< -o $@
|
||||
@gcc $(obj-flags) $< -o $@
|
||||
@printf "Done.\n"
|
||||
|
||||
$(obj) : bin/%.o : src/%.h
|
||||
$(obj) : bin/%.o : src/%.c
|
||||
@mkdir -p $(@D)
|
||||
@printf "Compiling object for $< ...\n"
|
||||
gcc $(obj-flags) $< -o $@
|
||||
@gcc $(obj-flags) $< -o $@
|
||||
@printf "Done.\n"
|
||||
|
||||
bin/%.so : include/%.h
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
|
||||
// TODO: I looked at https://man7.org/linux/man-pages/man3/cmsg.3.html to get this value, but I really don't know if it's right...
|
||||
#define CONTROL_BUFFER_SIZE CMSG_SPACE(sizeof(struct ucred))
|
||||
|
|
65
readme.md
65
readme.md
|
@ -42,29 +42,68 @@ You should be able to use these libraries in a variety of languages that support
|
|||
|
||||
I'll write up API documentation soon, but for now, look at the header files in `./include` for an idea of the API.
|
||||
|
||||
### Running the command line util
|
||||
## Running the command line util
|
||||
|
||||
There are subcommands for the server and the client respectively.
|
||||
This part is still a WIP, so don't expect much yet!
|
||||
|
||||
#### Server
|
||||
|
||||
```
|
||||
> localserv server <socket-path>
|
||||
```
|
||||
|
||||
The `<socket-path>` can be any file path that you have permissions to that isn't already occupied, e.g., `~/example.sock`
|
||||
### Server
|
||||
|
||||
The server listens for strings from clients, writes them to `stdout`, and waits for a line from `stdin` to send back to the client. This can be handled interactively, but it can also be hooked into with a script, as in the example `./server.sh`.
|
||||
|
||||
#### Client
|
||||
|
||||
```
|
||||
> localserv client <socket-path> <request>
|
||||
> localserv server [-u] [-a] <socket-path>
|
||||
```
|
||||
|
||||
The `<socket-path>` is the same one you specified when starting your server.
|
||||
The `<request>` is just any string you want to send to the server.
|
||||
- `<socket-path>`: any file path that you have permissions to that isn't already occupied, e.g., `~/example.sock`
|
||||
- `-u, --username`: flag to include username in request output.
|
||||
- `-a, --auth`: flag for auth mode (implies `--username`)
|
||||
|
||||
#### Auth mode
|
||||
|
||||
The server features an auth mode, in which it implements an additional protocol layer for enabling a simple form of authorization. In this mode, servers will accept three types of messages from clients.
|
||||
|
||||
##### Issue
|
||||
|
||||
**Request**:
|
||||
|
||||
```
|
||||
ISSUE
|
||||
```
|
||||
|
||||
**Response**: The server issues a JSON web token (JWT) which identifies the username of the client. This JWT has a short lifespan (15 seconds), and is primarily used to get a longer-lasting token via the "refresh" action. This action does not write anything to `stdout` or read anything from `stdin`.
|
||||
|
||||
##### Refresh
|
||||
|
||||
**Request**:
|
||||
|
||||
```
|
||||
REFRESH
|
||||
<JSON web token>
|
||||
```
|
||||
|
||||
**Response**: If the token is valid, server issues a new JSON web token (JWT) with the same username identified. This JWT has a long lifespan (10 minutes), and can be used with the "authorize" action to send messages to the server. This action does not write anything to `stdout` or read anything from `stdin`.
|
||||
|
||||
##### Issue
|
||||
|
||||
Message format:
|
||||
|
||||
```
|
||||
AUTHORIZE
|
||||
<JSON web token>
|
||||
<message>
|
||||
```
|
||||
|
||||
**Response**: If the token is valid, server conveys the username identified in the JWT and the corresponding message to `stdin`, and waits for a response from `stdout`, conveying it back to the client.
|
||||
|
||||
### Client
|
||||
|
||||
```
|
||||
> localserv client <socket-path> [<request>]
|
||||
```
|
||||
|
||||
- `<socket-path>`: the same path you specified when starting your server.
|
||||
- `<request>`: just any string you want to send to the server.
|
||||
|
||||
### Server script example
|
||||
|
||||
|
|
10
server.sh
10
server.sh
|
@ -9,9 +9,10 @@ if [[ -z "${1-}" ]]; then
|
|||
fi
|
||||
|
||||
# set up named pipes in same place as the socket
|
||||
recv="$1.recvpipe"
|
||||
resp="$1.resppipe"
|
||||
mkfifo $recv $resp
|
||||
recv="$1.in.pipe"
|
||||
resp="$1.out.pipe"
|
||||
# -m=600: make the pipes user permissions only
|
||||
mkfifo -m=600 $recv $resp
|
||||
# they will be removed when the script exits
|
||||
trap "rm -f $recv $resp" EXIT
|
||||
|
||||
|
@ -27,5 +28,4 @@ while read -r line; do
|
|||
done <$recv >$resp &
|
||||
|
||||
# run the server, hooked up to the pipes and at the socket address the user gave
|
||||
bin/localserv server $1 >$recv <$resp
|
||||
|
||||
bin/localserv server -a $1 >$recv <$resp
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
#include "interrupt.h"
|
||||
|
||||
volatile sig_atomic_t exit_flag = 0;
|
||||
|
||||
void interrupt_register_sighandler() {
|
||||
struct sigaction action;
|
||||
memset( &action, 0, sizeof(action) );
|
||||
action.sa_handler = interrupt_onsignal;
|
||||
sigfillset(&action.sa_mask);
|
||||
sigaction(SIGINT, &action, NULL);
|
||||
}
|
||||
|
||||
void interrupt_onsignal(int) {
|
||||
exit_flag = 1;
|
||||
}
|
||||
|
||||
int interrupt_return_value() {
|
||||
if (exit_flag == 1) {
|
||||
// print message if we exited through sigint
|
||||
fprintf(stderr, "\nExiting\n");
|
||||
}
|
||||
return exit_flag >= 0 ? 0 : exit_flag;
|
||||
}
|
||||
|
||||
int interrupt_should_exit() {
|
||||
return exit_flag;
|
||||
}
|
||||
|
||||
void interrupt_set_exit_flag() {
|
||||
exit_flag = -1;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef __INTERRUPT_H
|
||||
#define __INTERRUPT_H
|
||||
|
||||
void interrupt_register_sighandler();
|
||||
void interrupt_onsignal(int);
|
||||
int interrupt_return_value();
|
||||
int interrupt_should_exit();
|
||||
void interrupt_set_exit_flag();
|
||||
|
||||
#endif
|
279
src/localserv.c
279
src/localserv.c
|
@ -1,218 +1,83 @@
|
|||
#include "localserv.h"
|
||||
|
||||
char *exe_name;
|
||||
|
||||
int server(char *path, int jwt_mode) {
|
||||
int error;
|
||||
error = server_init(path);
|
||||
if (error) {
|
||||
fprintf(stderr, "server init failed: %s\n", strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
jwt_t *jwt = NULL;
|
||||
|
||||
if (jwt_mode) {
|
||||
error = jwt_new(&jwt);
|
||||
if (error) {
|
||||
fprintf(stderr, "JWT initialization failed: %s\n", strerror(error));
|
||||
return error;
|
||||
}
|
||||
jwt_set_alg(jwt, JWT_ALG_HS256, (unsigned char *)"THIS IS NOT A SAFE KEY! -- TODO: use a user-provided key instead", 64);
|
||||
if (error) {
|
||||
fprintf(stderr, "JWT initialization failed: %s\n", strerror(error));
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
int listen_fails = 0;
|
||||
|
||||
while (true) {
|
||||
error = server_listen();
|
||||
if (error) {
|
||||
fprintf(stderr, "listen failed: %s\n", strerror(error));
|
||||
if (++listen_fails > 5) {
|
||||
fprintf(stderr, "listen failed 5 times, exiting\n");
|
||||
if (jwt != NULL) jwt_free(jwt);
|
||||
return error;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
char* request = server_get_request();
|
||||
printf("%s\n", request);
|
||||
fflush(stdout);
|
||||
|
||||
char* response = NULL;
|
||||
|
||||
if (jwt_mode) {
|
||||
char *username = server_get_username(server_get_uid());
|
||||
if (username == NULL) {
|
||||
fprintf(stderr, "failed getting username\n");
|
||||
if (jwt != NULL) jwt_free(jwt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
jwt_del_grants(jwt, NULL);
|
||||
jwt_add_grant(jwt, "sub", username);
|
||||
jwt_add_grant_int(jwt, "iat", time(NULL));
|
||||
|
||||
response = jwt_encode_str(jwt);
|
||||
if (response == NULL) {
|
||||
fprintf(stderr, "failed emitting JWT\n");
|
||||
if (jwt != NULL) jwt_free(jwt);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
size_t len = 0;
|
||||
ssize_t length = getline(&response, &len, stdin);
|
||||
if (length == -1) {
|
||||
fprintf(stderr, "failed getting input\n");
|
||||
if (jwt != NULL) jwt_free(jwt);
|
||||
return -1;
|
||||
}
|
||||
response[length - 1] = '\0';
|
||||
}
|
||||
|
||||
error = server_respond(response);
|
||||
|
||||
if (error) {
|
||||
fprintf(stderr, "respond failed (will retry): %s\n", strerror(error));
|
||||
error = server_respond(response);
|
||||
if (error) {
|
||||
fprintf(stderr, "respond retry failed: %s\nfailed retry, exiting\n", strerror(error));
|
||||
free(response);
|
||||
if (jwt != NULL) jwt_free(jwt);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
free(response);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int client(char *path, char *message) {
|
||||
int error;
|
||||
error = client_init(path);
|
||||
if (error) {
|
||||
fprintf(stderr, "client init failed: %s\n", strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
error = client_request(message);
|
||||
if (error) {
|
||||
fprintf(stderr, "request failed: %s\n", strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
printf("%s\n", client_get_response());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int server_subcommand(opts opts, int argc, char *argv[]) {
|
||||
if (opts.help) {
|
||||
show_usage(stdout, server_usage);
|
||||
return 0;
|
||||
}
|
||||
else if (argc <= 1) {
|
||||
fprintf(stderr, "server: please provide a path\n\n");
|
||||
show_usage(stderr, server_usage);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *path = argv[1];
|
||||
|
||||
return server(path, opts.jwt);
|
||||
}
|
||||
|
||||
int client_subcommand(opts opts, int argc, char *argv[]) {
|
||||
if (opts.help) {
|
||||
show_usage(stdout, client_usage);
|
||||
return 0;
|
||||
}
|
||||
else if (opts.jwt) {
|
||||
fprintf(stderr, "client: invalid flag: “-j/--jwt” only applies to server subcommand\n\n");
|
||||
show_usage(stderr, server_usage);
|
||||
return -1;
|
||||
}
|
||||
else if (argc <= 1) {
|
||||
fprintf(stderr, "client: please provide a path\n\n");
|
||||
show_usage(stderr, client_usage);
|
||||
return -1;
|
||||
}
|
||||
else if (argc <= 2) {
|
||||
fprintf(stderr, "client: please provide a message\n\n");
|
||||
show_usage(stderr, client_usage);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return client(argv[1], argv[2]);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
interrupt_register_sighandler();
|
||||
set_exe_name(argv[0]);
|
||||
|
||||
// OSSL_DECODER_CTX_new_for_pkey()
|
||||
// BIO_new_file()
|
||||
// OSSL_DECODER_from_bio()
|
||||
|
||||
if (argc <= 1) { // no subcommand!
|
||||
fprintf(stderr, "Please provide a subcommand.\n\n");
|
||||
show_usage(stderr, both_usage);
|
||||
return -1;
|
||||
}
|
||||
|
||||
opts opts = {
|
||||
.help = 0,
|
||||
.jwt = 0,
|
||||
};
|
||||
|
||||
static struct option help_opt[] = {
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"jwt", no_argument, NULL, 'j'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
int option_index = 0;
|
||||
// // Configures getopt to not print its own error message
|
||||
// opterr = 0;
|
||||
int optchar;
|
||||
while ((optchar = getopt_long(argc, argv, "hj", help_opt, &option_index)) != -1) {
|
||||
switch (optchar) {
|
||||
case 'h':
|
||||
opts.help = 1;
|
||||
return 0;
|
||||
case 'j':
|
||||
opts.jwt = 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
char *subcommand = argv[optind];
|
||||
if (strcmp(subcommand, "server") == 0) {
|
||||
return server_subcommand(opts, argc - optind, argv + optind);
|
||||
}
|
||||
else if (strcmp(subcommand, "client") == 0) {
|
||||
return client_subcommand(opts, argc - optind, argv + optind);
|
||||
}
|
||||
else if (opts.help) {
|
||||
show_usage(stdout, both_usage);
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, "Subcommand “%s” not recognized; provide a valid subcommand as the first argument.\n\n", subcommand);
|
||||
}
|
||||
else if (opts.help) {
|
||||
show_usage(stdout, both_usage);
|
||||
return 0;
|
||||
interrupt_set_exit_flag();
|
||||
}
|
||||
else {
|
||||
// if optind is equal to (or greater than, which probably won't happen) the number of args we got,
|
||||
// it means we only got options, no positional args
|
||||
fprintf(stderr, "Please provide a subcommand.\n\n");
|
||||
opts opts = {
|
||||
.help = 0,
|
||||
.username = 0,
|
||||
.auth = 0,
|
||||
.delims = { '\t', '\n', '\n' },
|
||||
};
|
||||
|
||||
static struct option help_opt[] = {
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"username", no_argument, NULL, 'u'},
|
||||
{"auth", no_argument, NULL, 'a'},
|
||||
// TODO: args for delims
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
int option_index = 0;
|
||||
// // Configures getopt to not print its own error message
|
||||
// opterr = 0;
|
||||
int optchar;
|
||||
while ((optchar = getopt_long(argc, argv, "hua", help_opt, &option_index)) != -1) {
|
||||
switch (optchar) {
|
||||
case 'h':
|
||||
opts.help = 1;
|
||||
break;
|
||||
case 'u':
|
||||
opts.username = 1;
|
||||
break;
|
||||
case 'a':
|
||||
opts.auth = 1;
|
||||
// authmode implies username
|
||||
opts.username = 1;
|
||||
break;
|
||||
// TODO: delims
|
||||
default:
|
||||
interrupt_set_exit_flag();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!interrupt_should_exit()) {
|
||||
if (optind < argc) {
|
||||
char *subcommand = argv[optind];
|
||||
if (strcmp(subcommand, "server") == 0) {
|
||||
server_subcommand(opts, argc - optind, argv + optind);
|
||||
}
|
||||
else if (strcmp(subcommand, "client") == 0) {
|
||||
client_subcommand(opts, argc - optind, argv + optind);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Subcommand “%s” not recognized; provide a valid subcommand as the first argument.\n\n", subcommand);
|
||||
show_usage(true);
|
||||
interrupt_set_exit_flag();
|
||||
}
|
||||
}
|
||||
else if (opts.help) {
|
||||
show_usage(false);
|
||||
}
|
||||
else {
|
||||
// if optind is equal to (or greater than, which probably won't happen) the number of args we got,
|
||||
// it means we only got options, no positional args
|
||||
fprintf(stderr, "Please provide a subcommand.\n\n");
|
||||
show_usage(true);
|
||||
interrupt_set_exit_flag();
|
||||
}
|
||||
}
|
||||
}
|
||||
show_usage(stderr, both_usage);
|
||||
return -1;
|
||||
|
||||
return interrupt_return_value();
|
||||
}
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
#include "server.h"
|
||||
#include "client.h"
|
||||
#include "subcommand/server.h"
|
||||
#include "subcommand/client.h"
|
||||
#include "usage.h"
|
||||
#include "interrupt.h"
|
||||
#include "opts.h"
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
#include <jwt.h>
|
||||
|
||||
typedef struct {
|
||||
int help;
|
||||
int jwt;
|
||||
} opts;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef __OPTS_H
|
||||
#define __OPTS_H
|
||||
|
||||
typedef struct opts {
|
||||
int help;
|
||||
int username;
|
||||
int auth;
|
||||
char delims[3];
|
||||
} opts;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,126 @@
|
|||
#include "auth.h"
|
||||
|
||||
#define ISSUE "ISSUE"
|
||||
#define REFRESH "REFRESH\n"
|
||||
#define REFRESH_OFFSET sizeof(REFRESH) - 1
|
||||
#define AUTHORIZE "AUTHORIZE\n"
|
||||
#define AUTHORIZE_OFFSET sizeof(AUTHORIZE) - 1
|
||||
#define ISSUE_EXPIRATION 15
|
||||
#define REFRESH_EXPIRATION 1200
|
||||
#define grant_failure(grant) fprintf(stderr, "JWT %s set failed: %s\n", grant, strerror(error))
|
||||
|
||||
unsigned char *key = (unsigned char *)"THIS IS NOT A SAFE KEY! -- TODO: use a user-provided key instead";
|
||||
int keylen = 64;
|
||||
|
||||
int set_jwt_grants(jwt_t *jwt, const char *sub, const time_t exp) {
|
||||
int error = jwt_set_alg(jwt, JWT_ALG_HS256, key, keylen);
|
||||
if (error) {
|
||||
grant_failure("alg");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = jwt_add_grant(jwt, "sub", sub);
|
||||
if (error) {
|
||||
grant_failure("sub");
|
||||
return error;
|
||||
}
|
||||
|
||||
time_t iat = time(NULL);
|
||||
error = jwt_add_grant_int(jwt, "iat", iat);
|
||||
if (error) {
|
||||
grant_failure("iat");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = jwt_add_grant_int(jwt, "exp", iat + exp);
|
||||
if (error) {
|
||||
grant_failure("exp");
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* auth_issue_jwt(const char *sub, const time_t exp) {
|
||||
jwt_t *jwt = NULL;
|
||||
|
||||
int error = jwt_new(&jwt);
|
||||
char *response = NULL;
|
||||
if (error) {
|
||||
fprintf(stderr, "JWT initialization failed: %s\n", strerror(error));
|
||||
return NULL;
|
||||
}
|
||||
error = set_jwt_grants(jwt, sub, exp);
|
||||
if (error) {
|
||||
jwt_free(jwt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
response = jwt_encode_str(jwt);
|
||||
jwt_free(jwt);
|
||||
if (response == NULL) {
|
||||
fprintf(stderr, "failed emitting JWT: %s\n", strerror(errno));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
char* auth_verify_jwt(const char *jwt_str, time_t now, char **sub) {
|
||||
jwt_t *jwt;
|
||||
int error = jwt_decode(&jwt, jwt_str, key, keylen);
|
||||
if (error) {
|
||||
fprintf(stderr, "JWT validation failed: %s\n", strerror(error));
|
||||
// TODO: determine which errnos correspond to invalid credentials
|
||||
// and which correspond to server errors
|
||||
// and then return the right thing here
|
||||
return strdup("Bad Credential");
|
||||
}
|
||||
time_t exp = jwt_get_grant_int(jwt, "exp");
|
||||
if (errno == ENOENT) {
|
||||
return strdup("Bad Credential");
|
||||
}
|
||||
else if (now >= exp) {
|
||||
return strdup("Token Expired");
|
||||
}
|
||||
*sub = jwt_get_grant(jwt, "sub");
|
||||
if (sub == NULL) {
|
||||
return strdup("Bad Credential");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* auth(char **request, char **username) {
|
||||
time_t now = time(NULL);
|
||||
if (strcasecmp(*request, ISSUE) == 0) {
|
||||
char *response = auth_issue_jwt(*username, ISSUE_EXPIRATION);
|
||||
return response == NULL ? strdup("Internal Server Error") : response;
|
||||
}
|
||||
else if (strncasecmp(*request, REFRESH, REFRESH_OFFSET) == 0) {
|
||||
char *response = auth_verify_jwt(*request + REFRESH_OFFSET, now, username);
|
||||
if (response == NULL) {
|
||||
if (*username == NULL) {
|
||||
return strdup("Internal Server Error");
|
||||
}
|
||||
response = auth_issue_jwt(*username, REFRESH_EXPIRATION);
|
||||
}
|
||||
return response == NULL ? strdup("Internal Server Error") : response;
|
||||
}
|
||||
else if (strncasecmp(*request, AUTHORIZE, AUTHORIZE_OFFSET) == 0) {
|
||||
char *newline = strchr(*request + AUTHORIZE_OFFSET, '\n');
|
||||
if (newline != NULL) {
|
||||
// usually there should be a message after the token.
|
||||
// if so, we replace this newline with a null char so JWT parsing works
|
||||
*newline = '\0';
|
||||
}
|
||||
char *response = auth_verify_jwt(*request + AUTHORIZE_OFFSET, now, username);
|
||||
if (*username == NULL) {
|
||||
return strdup("Internal Server Error");
|
||||
}
|
||||
// response NULL means we verified!
|
||||
// pass along request by moving past auth stuff
|
||||
if (response == NULL) {
|
||||
*request = newline == NULL ? "" : newline + 1;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
return strdup("Bad Request");
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <jwt.h>
|
||||
|
||||
char* auth(char **request, char **username);
|
|
@ -0,0 +1,46 @@
|
|||
#include "client.h"
|
||||
|
||||
void client(char *path, char *message, opts *opts) {
|
||||
int error;
|
||||
error = client_init(path);
|
||||
if (error) {
|
||||
fprintf(stderr, "client init failed: %s\n", strerror(error));
|
||||
interrupt_set_exit_flag();
|
||||
return;
|
||||
}
|
||||
|
||||
error = client_request(message);
|
||||
if (error) {
|
||||
fprintf(stderr, "request failed: %s\n", strerror(error));
|
||||
interrupt_set_exit_flag();
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s\n", client_get_response());
|
||||
}
|
||||
|
||||
void client_subcommand(opts opts, int argc, char *argv[]) {
|
||||
set_usage_context(usage_client);
|
||||
|
||||
char *message = "";
|
||||
if (opts.help) {
|
||||
show_usage(false);
|
||||
}
|
||||
else if (opts.username) {
|
||||
fprintf(stderr, "client: invalid flag: “-u/--username” only applies to server subcommand\n\n");
|
||||
show_usage(true);
|
||||
interrupt_set_exit_flag();
|
||||
}
|
||||
else if (argc <= 1) {
|
||||
fprintf(stderr, "client: please provide a path\n\n");
|
||||
show_usage(true);
|
||||
interrupt_set_exit_flag();
|
||||
}
|
||||
else {
|
||||
if (argc > 2) {
|
||||
// message provided
|
||||
message = argv[2];
|
||||
}
|
||||
client(argv[1], message, &opts);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#include "../../include/client.h"
|
||||
#include "../opts.h"
|
||||
#include "../usage.h"
|
||||
#include "../interrupt.h"
|
||||
|
||||
void client_subcommand(opts opts, int argc, char *argv[]);
|
|
@ -0,0 +1,100 @@
|
|||
#include "server.h"
|
||||
|
||||
void server(char *path, opts *opts) {
|
||||
int error;
|
||||
error = server_init(path);
|
||||
if (error) {
|
||||
fprintf(stderr, "server init failed: %s\n", strerror(error));
|
||||
interrupt_set_exit_flag();
|
||||
}
|
||||
|
||||
int listen_fails = 0;
|
||||
char* response = NULL;
|
||||
|
||||
while (!interrupt_should_exit()) {
|
||||
error = server_listen();
|
||||
if (error) {
|
||||
if (interrupt_should_exit()) {
|
||||
// no message necessary
|
||||
break;
|
||||
}
|
||||
fprintf(stderr, "listen failed: %s\n", strerror(error));
|
||||
if (++listen_fails > 5) {
|
||||
fprintf(stderr, "listen failed 5 times, exiting\n");
|
||||
interrupt_set_exit_flag();
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// figure out username
|
||||
char *username = NULL;
|
||||
if ((*opts).username) {
|
||||
username = server_get_username(server_get_uid());
|
||||
if (username == NULL) {
|
||||
fprintf(stderr, "failed getting username\n");
|
||||
interrupt_set_exit_flag();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char *request = server_get_request();
|
||||
if ((*opts).auth) {
|
||||
response = auth(&request, &username);
|
||||
}
|
||||
|
||||
// if auth supplied us with a response,
|
||||
// no need to do the interactive loop
|
||||
if (response == NULL) {
|
||||
// write request to stdout
|
||||
if (username) {
|
||||
printf("%s%c", username, (*opts).delims[0]);
|
||||
}
|
||||
printf("%s%c", request, (*opts).delims[1]);
|
||||
fflush(stdout);
|
||||
|
||||
// read user input
|
||||
size_t len = 0;
|
||||
ssize_t length = getdelim(&response, &len, (*opts).delims[2], stdin);
|
||||
if (length == -1) {
|
||||
fprintf(stderr, "failed getting input\n");
|
||||
interrupt_set_exit_flag();
|
||||
break;
|
||||
}
|
||||
response[length - 1] = '\0';
|
||||
}
|
||||
|
||||
error = server_respond(response);
|
||||
|
||||
if (error) {
|
||||
fprintf(stderr, "respond failed (will retry): %s\n", strerror(error));
|
||||
error = server_respond(response);
|
||||
if (error) {
|
||||
fprintf(stderr, "respond retry failed: %s\nfailed retry, exiting\n", strerror(error));
|
||||
interrupt_set_exit_flag();
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(response);
|
||||
response = NULL;
|
||||
}
|
||||
free(response);
|
||||
}
|
||||
|
||||
void server_subcommand(opts opts, int argc, char *argv[]) {
|
||||
set_usage_context(usage_server);
|
||||
|
||||
if (opts.help) {
|
||||
show_usage(false);
|
||||
}
|
||||
else if (argc <= 1) {
|
||||
fprintf(stderr, "server: please provide a path\n\n");
|
||||
show_usage(true);
|
||||
interrupt_set_exit_flag();
|
||||
}
|
||||
else {
|
||||
char *path = argv[1];
|
||||
server(path, &opts);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include "../../include/server.h"
|
||||
#include "../opts.h"
|
||||
#include "../usage.h"
|
||||
#include "../interrupt.h"
|
||||
#include "auth.h"
|
||||
#include <stdint.h>
|
||||
|
||||
void server_subcommand(opts opts, int argc, char *argv[]);
|
28
src/usage.c
28
src/usage.c
|
@ -1,14 +1,17 @@
|
|||
#include "usage.h"
|
||||
|
||||
const char server_callstring[] = "server [-j] <path>";
|
||||
const char server_callstring[] = "server [-h] [-u] [-a] <path>";
|
||||
const char server_helpstring[] =
|
||||
" <path>: filesystem path at which to create server Unix domain socket\n"
|
||||
" -j, --jwt: run server in JWT mode\n";
|
||||
" -u, --username: include username in request output\n"
|
||||
" -a, --auth: run server in auth mode (implies --username)\n"
|
||||
" -h, --help: show usage message\n";
|
||||
|
||||
const char client_callstring[] = "client <path> <message>";
|
||||
const char client_callstring[] = "client [-h] <path> [<message>]";
|
||||
const char client_helpstring[] =
|
||||
" <path>: filesystem path of target server’s socket\n"
|
||||
" <message>: string to send to server\n";
|
||||
" <message>: string to send to server\n"
|
||||
" -h, --help: show usage message\n";
|
||||
|
||||
const char *my_name = NULL;
|
||||
|
||||
|
@ -19,12 +22,20 @@ void set_exe_name(char *argv0) {
|
|||
}
|
||||
}
|
||||
|
||||
void show_usage(FILE *file, usage_type usage) {
|
||||
usage_type usage_context = usage_both;
|
||||
|
||||
void set_usage_context(usage_type usage) {
|
||||
usage_context = usage;
|
||||
}
|
||||
|
||||
void show_usage(int is_error) {
|
||||
if (my_name == NULL) {
|
||||
my_name = "localserv";
|
||||
}
|
||||
|
||||
if (usage == server_usage) {
|
||||
FILE *file = is_error ? stderr : stdout;
|
||||
|
||||
if (usage_context == usage_server) {
|
||||
fprintf(
|
||||
file,
|
||||
"usage: %s %s\n\n%s\n",
|
||||
|
@ -32,7 +43,7 @@ void show_usage(FILE *file, usage_type usage) {
|
|||
server_callstring,
|
||||
server_helpstring
|
||||
);
|
||||
} else if (usage == client_usage) {
|
||||
} else if (usage_context == usage_client) {
|
||||
fprintf(
|
||||
file,
|
||||
"usage: %s %s\n\n%s\n",
|
||||
|
@ -41,7 +52,7 @@ void show_usage(FILE *file, usage_type usage) {
|
|||
client_helpstring
|
||||
);
|
||||
|
||||
} else { // usage == both_usage
|
||||
} else { // usage_context == usage_both
|
||||
fprintf(
|
||||
file,
|
||||
"usage:\n"
|
||||
|
@ -58,5 +69,4 @@ void show_usage(FILE *file, usage_type usage) {
|
|||
client_helpstring
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
10
src/usage.h
10
src/usage.h
|
@ -1,7 +1,13 @@
|
|||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum { server_usage, client_usage, both_usage } usage_type;
|
||||
#ifndef __USAGE_H
|
||||
#define __USAGE_H
|
||||
|
||||
typedef enum { usage_server, usage_client, usage_both } usage_type;
|
||||
|
||||
void set_exe_name(char *argv0);
|
||||
void show_usage(FILE *file, usage_type usage);
|
||||
void set_usage_context(usage_type usage);
|
||||
void show_usage(int is_error);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue