#include #include #include #include #include #include #include #include #include #include "./slice.h" #define MAX_LINE 4096 #define MAX_USERNAME 256 #define AUTH_FILE "/etc/marrow/authorized_keys" #ifndef PATH_MAX #define PATH_MAX 4096 #endif static int32_t _openat(const int32_t wfd, const struct slice *path, int32_t flags) { char cpath[PATH_MAX]; if (path->size >= PATH_MAX) return -1; memcpy(cpath, path->ptr, path->size); cpath[path->size] = '\0'; return openat(wfd, cpath, flags); } // TODO: slice delim variant static struct slice _sltok_r(const struct slice *src, const char delim, struct slice *rest) { struct slice result; size_t i; result.ptr = src->ptr; result.size = 0; rest->ptr = src->ptr; rest->size = src->size; i = 0; while (i < src->size) { ++rest->ptr; --rest->size; if (delim == src->ptr[i++]) { break; } ++result.size; } return (result); } static int32_t _slcmp(const struct slice *a, const struct slice *b) { int32_t rc; if (a->size < b->size) { rc = strncmp(a->ptr, b->ptr, a->size); if (0 == rc) { rc = -1; } } else if (a->size > b->size) { rc = strncmp(a->ptr, b->ptr, b->size); if (0 == rc) { rc = 1; } } else { rc = strncmp(a->ptr, b->ptr, a->size); } return (rc); } static int32_t _readline(struct slice *dest, const int32_t fd, char *buffer, const size_t sz_buffer) { // TODO: make this read in chunks, and store the context in a struct ssize_t sz_read; char c; dest->ptr = buffer; dest->size = 0; while (dest->size < (sz_buffer - 1)) { sz_read = read(fd, &c, 1); if (-1 == sz_read) { perror("read"); // TODO: handle error return (-1); } if (0 == sz_read) { break; } if ('\n' == c) { if (0 < dest->size) { break; } continue; } buffer[dest->size++] = c; } return (0); } int32_t main(int32_t argc, char *argv[]) { int32_t fd; // usage: marrow-auth /usr/local/etc/marrow %t %k if (argc < 3) { dprintf(STDERR_FILENO, "usage: marrow-auth config_dir key_type base64_key\n"); return (EXIT_FAILURE); } // TODO: loop over config dir per user const struct slice conf_path = slice_fromcstr(argv[1]); const struct slice ssh_keytype = slice_fromcstr(argv[2]); const struct slice ssh_key = slice_fromcstr(argv[3]); fd = _openat(AT_FDCWD, &conf_path, O_RDONLY); if (0 > fd) { perror("openat"); return (EXIT_FAILURE); } char buf[4096]; long basep = 0; int nbytes; int32_t match = 0; while ((nbytes = getdirentries(fd, buf, sizeof(buf), &basep)) > 0) { char *ptr = buf; while (ptr < buf + nbytes) { struct dirent *dp = (struct dirent *)ptr; if (dp->d_reclen == 0) break; ptr += dp->d_reclen; if (0 == strcmp(".", dp->d_name) || 0 == strcmp("..", dp->d_name)) { continue; } // TODO: check each line in configuration file (key per line) int32_t auth_fd = openat(fd, dp->d_name, O_RDONLY); char key_buffer[8192]; struct slice key; int32_t rc; while (!match) { rc = _readline(&key, auth_fd, key_buffer, sizeof(key_buffer)); if (-1 == rc || 0 == key.size) { break; } struct slice rest; struct slice origin_keytype; struct slice origin_key; struct slice origin_comment; origin_keytype = _sltok_r(&key, ' ', &rest); key = rest; origin_key = _sltok_r(&key, ' ', &rest); key = rest; origin_comment = _sltok_r(&key, ' ', &rest); if (0 == _slcmp(&ssh_keytype, &origin_keytype) && 0 == _slcmp(&ssh_key, &origin_key)) { // TODO: if match, return ssh authorized_key format for that user printf("command=\"/usr/local/bin/marrow-shell -u %s\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding %.*s %.*s %.*s\n", dp->d_name, (int32_t)ssh_keytype.size, ssh_keytype.ptr, (int32_t)ssh_key.size, ssh_key.ptr, (int32_t)origin_comment.size, origin_comment.ptr); match = 1; } } close(auth_fd); if (match) { break; } } } close(fd); // TODO: if no match, return ssh authorized_key format if (!match) { printf("command=\"/usr/local/bin/marrow-shell\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding %.*s %.*s anonymous\n", (int32_t)ssh_keytype.size, ssh_keytype.ptr, (int32_t)ssh_key.size, ssh_key.ptr ); } return (EXIT_SUCCESS); }