git / brickware / marrow.git - 2d1c595

(2 months ago)commit 512c504: Slice forward approach

tree / marrow-auth.c

marrow-auth.c

raw

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>

#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);    
}