(2 months ago)commit 512c504: Slice forward approach
| author | Tanner Stenson <tanner@brickware.sh> |
| date | Tue Oct 28 22:59:43 2025 -0400 |
Slice forward approach
commit 512c504b4e8dd77984078c1a2eb7a212b6fa27dd
Author: Tanner Stenson <tanner@brickware.sh>
Date: Tue Oct 28 22:59:43 2025 -0400
Slice forward approach
diff --git a/marrow-auth.c b/marrow-auth.c
index 4f770fa..45b7474 100644
--- a/marrow-auth.c
+++ b/marrow-auth.c
@@ -124,9 +124,9 @@ main(int32_t argc, char *argv[]) {
}
// TODO: loop over config dir per user
- const struct slice conf_path = cstrtoslice(argv[1]);
- const struct slice ssh_keytype = cstrtoslice(argv[2]);
- const struct slice ssh_key = cstrtoslice(argv[3]);
+ 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) {
@@ -180,11 +180,11 @@ main(int32_t argc, char *argv[]) {
// 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,
- ssh_keytype.size,
+ (int32_t)ssh_keytype.size,
ssh_keytype.ptr,
- ssh_key.size,
+ (int32_t)ssh_key.size,
ssh_key.ptr,
- origin_comment.size,
+ (int32_t)origin_comment.size,
origin_comment.ptr);
match = 1;
}
@@ -203,9 +203,9 @@ main(int32_t argc, char *argv[]) {
// 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",
- ssh_keytype.size,
+ (int32_t)ssh_keytype.size,
ssh_keytype.ptr,
- ssh_key.size,
+ (int32_t)ssh_key.size,
ssh_key.ptr
);
}
diff --git a/marrow-shell.c b/marrow-shell.c
index c0cd2f3..ffbf82e 100644
--- a/marrow-shell.c
+++ b/marrow-shell.c
@@ -8,6 +8,8 @@
#include <errno.h>
#include <stdint.h>
+#include "./slice.h"
+
#define MAX_CMD_LEN 4096
#define GIT_BASE_PATH "/srv/git"
@@ -25,7 +27,7 @@ get_git_user(void) {
static int32_t
is_anonymous(void) {
- return NULL == get_git_user();
+ return 0x00 == get_git_user();
}
static git_command_t
@@ -50,46 +52,46 @@ is_write_command(git_command_t cmd) {
return (CMD_GIT_RECEIVE_PACK == cmd);
}
-static char *
+static struct slice
extract_repo_path(const char *cmd) {
- char *path = NULL;
const char *start = strchr(cmd, '\'');
-
+ struct slice result = {
+ .ptr = 0x00,
+ .size = 0,
+ };
+
if (start) {
start++;
const char *end = strchr(start, '\'');
if (end) {
- size_t len = end - start;
- path = malloc(len + 1);
- if (path) {
- strncpy(path, start, len);
- path[len] = '\0';
- }
+ result.ptr = start;
+ result.size = end - start;
}
} else {
const char *space = strchr(cmd, ' ');
if (space) {
space++;
while (' ' == *space) space++;
-
- size_t len = strlen(space);
- path = malloc(len + 1);
- if (path) {
- strcpy(path, space);
- char *end = strchr(path, ' ');
- if (end) *end = '\0';
+
+ const char *end = strchr(space, ' ');
+ if (end) {
+ result.ptr = space;
+ result.size = end - space;
+ } else {
+ result.ptr = space;
+ result.size = strlen(space);
}
}
}
-
- return (path);
+
+ return (result);
}
static char *
normalize_path(const char *path) {
char *normalized = malloc(PATH_MAX);
if (!normalized) {
- return NULL;
+ return 0x00;
}
char temp[PATH_MAX];
@@ -114,7 +116,7 @@ normalize_path(const char *path) {
/* Normal component */
components[comp_count++] = token;
}
- token = strtok(NULL, "/");
+ token = strtok(0x00, "/");
}
/* Rebuild the path */
@@ -133,17 +135,18 @@ normalize_path(const char *path) {
}
static int32_t
-validate_repo_path(const char *repo_path, const char *user) {
+validate_repo_path(const struct slice repo_path, const struct slice user) {
char real_path[PATH_MAX];
char expected_base[PATH_MAX];
- if (0 == strncmp(repo_path, GIT_BASE_PATH, strlen(GIT_BASE_PATH))) {
- snprintf(real_path, sizeof(real_path), "%s", repo_path);
+ if (repo_path.size >= strlen(GIT_BASE_PATH) &&
+ 0 == strncmp(repo_path.ptr, GIT_BASE_PATH, strlen(GIT_BASE_PATH))) {
+ snprintf(real_path, sizeof(real_path), "%.*s", (int32_t)repo_path.size, repo_path.ptr);
} else {
- if ('/' == repo_path[0]) {
- snprintf(real_path, sizeof(real_path), "%s%s", GIT_BASE_PATH, repo_path);
+ if (repo_path.size > 0 && '/' == repo_path.ptr[0]) {
+ snprintf(real_path, sizeof(real_path), "%s%.*s", GIT_BASE_PATH, (int32_t)repo_path.size, repo_path.ptr);
} else {
- snprintf(real_path, sizeof(real_path), "%s/%s", GIT_BASE_PATH, repo_path);
+ snprintf(real_path, sizeof(real_path), "%s/%.*s", GIT_BASE_PATH, (int32_t)repo_path.size, repo_path.ptr);
}
}
@@ -153,14 +156,14 @@ validate_repo_path(const char *repo_path, const char *user) {
return 0;
}
- if (!user) {
+ if (slice_isempty(user)) {
snprintf(expected_base, sizeof(expected_base), "%s/", GIT_BASE_PATH);
} else {
- snprintf(expected_base, sizeof(expected_base), "%s/%s/", GIT_BASE_PATH, user);
+ snprintf(expected_base, sizeof(expected_base), "%s/%.*s/", GIT_BASE_PATH, (int32_t)user.size, user.ptr);
}
int32_t valid = (0 == strncmp(resolved, expected_base, strlen(expected_base))) ||
- (0 == strcmp(resolved, GIT_BASE_PATH) && !user);
+ (0 == strcmp(resolved, GIT_BASE_PATH) && slice_isempty(user));
free(resolved);
@@ -168,55 +171,44 @@ validate_repo_path(const char *repo_path, const char *user) {
}
static int32_t
-check_permissions(const char *cmd) {
+check_permissions(const struct slice *user, const char *cmd) {
git_command_t git_cmd = parse_command(cmd);
-
+
if (CMD_UNKNOWN == git_cmd) {
- fputs("Error: Unknown command\n", stderr);
+ dprintf(STDERR_FILENO, "Error: Unknown command\n");
return 0;
}
-
- const char *user = get_git_user();
- char *repo_path = extract_repo_path(cmd);
-
- if (!repo_path) {
- fputs("Error: Could not extract repository path\n", stderr);
+
+ struct slice repo_path = extract_repo_path(cmd);
+ if (slice_isempty(repo_path)) {
+ dprintf(STDERR_FILENO, "Error: Could not extract repository path\n");
return 0;
}
-
- if (is_anonymous()) {
+
+ if (slice_isempty(*user)) { /* anonymous */
if (is_write_command(git_cmd)) {
- fputs("Error: Anonymous users cannot perform write operations\n", stderr);
- free(repo_path);
+ dprintf(STDERR_FILENO, "Error: Anonymous users cannot perform write operations\n");
return 0;
}
} else {
if (is_write_command(git_cmd)) {
- if (!validate_repo_path(repo_path, user)) {
- fputs("Error: User '", stderr);
- fputs(user, stderr);
- fputs("' can only write to repositories under ", stderr);
- fputs(GIT_BASE_PATH, stderr);
- fputs("/", stderr);
- fputs(user, stderr);
- fputs("/\n", stderr);
- free(repo_path);
+ if (!validate_repo_path(repo_path, *user)) {
+ dprintf(STDERR_FILENO, "Error: User '%.*s' can only write to repositories under %s/%.*s/\n",
+ (int32_t)user->size, user->ptr,
+ GIT_BASE_PATH,
+ (int32_t)user->size, user->ptr);
return 0;
}
}
}
-
+
if (is_read_command(git_cmd)) {
- if (!validate_repo_path(repo_path, NULL)) {
- fputs("Error: Repository path must be under ", stderr);
- fputs(GIT_BASE_PATH, stderr);
- fputs("/\n", stderr);
- free(repo_path);
+ if (!validate_repo_path(repo_path, slice_empty())) {
+ dprintf(STDERR_FILENO, "Error: Repository path must be under %s/\n", GIT_BASE_PATH);
return 0;
}
}
-
- free(repo_path);
+
return 1;
}
@@ -248,21 +240,22 @@ static void
execute_command(const char *cmd) {
char command[MAX_CMD_LEN];
git_command_t git_cmd = parse_command(cmd);
- char *repo_path = extract_repo_path(cmd);
+ struct slice repo_path = extract_repo_path(cmd);
- if (!repo_path) {
- fputs("Error: Invalid command format\n", stderr);
+ if (slice_isempty(repo_path)) {
+ dprintf(STDERR_FILENO, "Error: Invalid command format\n");
exit(1);
}
char full_path[PATH_MAX];
- if (0 == strncmp(repo_path, GIT_BASE_PATH, strlen(GIT_BASE_PATH))) {
- snprintf(full_path, sizeof(full_path), "%s", repo_path);
+ if (repo_path.size >= strlen(GIT_BASE_PATH) &&
+ 0 == strncmp(repo_path.ptr, GIT_BASE_PATH, strlen(GIT_BASE_PATH))) {
+ snprintf(full_path, sizeof(full_path), "%.*s", (int32_t)repo_path.size, repo_path.ptr);
} else {
- if ('/' == repo_path[0]) {
- snprintf(full_path, sizeof(full_path), "%s%s", GIT_BASE_PATH, repo_path);
+ if (repo_path.size > 0 && '/' == repo_path.ptr[0]) {
+ snprintf(full_path, sizeof(full_path), "%s%.*s", GIT_BASE_PATH, (int32_t)repo_path.size, repo_path.ptr);
} else {
- snprintf(full_path, sizeof(full_path), "%s/%s", GIT_BASE_PATH, repo_path);
+ snprintf(full_path, sizeof(full_path), "%s/%.*s", GIT_BASE_PATH, (int32_t)repo_path.size, repo_path.ptr);
}
}
@@ -272,18 +265,16 @@ execute_command(const char *cmd) {
if (0 != stat(full_path, &st)) {
if (ENOENT == errno) {
if (!create_bare_repository(full_path)) {
- fputs("Error: Failed to create repository\n", stderr);
- free(repo_path);
+ dprintf(STDERR_FILENO, "Error: Failed to create repository\n");
exit(1);
}
} else {
perror("stat");
- free(repo_path);
exit(1);
}
}
}
-
+
switch (git_cmd) {
case CMD_GIT_UPLOAD_PACK:
snprintf(command, sizeof(command), "git-upload-pack '%s'", full_path);
@@ -295,34 +286,29 @@ execute_command(const char *cmd) {
snprintf(command, sizeof(command), "git-upload-archive '%s'", full_path);
break;
default:
- fputs("Error: Unknown git command\n", stderr);
- free(repo_path);
+ dprintf(STDERR_FILENO, "Error: Unknown git command\n");
exit(1);
}
-
- free(repo_path);
-
- execl("/bin/sh", "sh", "-c", command, NULL);
+
+ execl("/bin/sh", "sh", "-c", command, (char *)0x00);
perror("execl");
exit(1);
}
int32_t
main(int32_t argc, char *argv[]) {
- const char *username = NULL;
+ struct slice user = slice_empty();
int32_t arg_offset = 0;
/* Check for -u USER flag for authenticated access */
if (argc > 2 && 0 == strcmp(argv[1], "-u")) {
- username = argv[2];
+ user = slice_fromcstr(argv[2]);
arg_offset = 2;
- /* Set GIT_USER environment variable */
- setenv("GIT_USER", username, 1);
}
/* Check for SSH_ORIGINAL_COMMAND first (SSH forced command) */
const char *ssh_cmd = getenv("SSH_ORIGINAL_COMMAND");
- const char *cmd = NULL;
+ const char *cmd = 0x00;
if (ssh_cmd) {
cmd = ssh_cmd;
@@ -330,29 +316,24 @@ main(int32_t argc, char *argv[]) {
/* Fallback to command line argument */
cmd = argv[2 + arg_offset];
} else {
- fputs("Usage: ", stderr);
- fputs(argv[0], stderr);
- fputs(" [-u USER] -c <command>\n", stderr);
- fputs("This shell is designed to work with git SSH access\n", stderr);
- fputs("Usually invoked via SSH with SSH_ORIGINAL_COMMAND set\n", stderr);
- fputs("Supported commands:\n", stderr);
- fputs(" git-upload-pack <repo> - Clone/fetch repository\n", stderr);
- fputs(" git-receive-pack <repo> - Push to repository\n", stderr);
- fputs(" git-upload-archive <repo> - Archive repository\n", stderr);
+ dprintf(STDERR_FILENO, "Usage: %s", argv[0]);
+ dprintf(STDERR_FILENO, " [-u USER] -c <command>\n");
+ dprintf(STDERR_FILENO, "This shell is designed to work with git SSH access\n");
+ dprintf(STDERR_FILENO, "Usually invoked via SSH with SSH_ORIGINAL_COMMAND set\n");
+ dprintf(STDERR_FILENO, "Supported commands:\n");
+ dprintf(STDERR_FILENO, " git-upload-pack <repo> - Clone/fetch repository\n");
+ dprintf(STDERR_FILENO, " git-receive-pack <repo> - Push to repository\n");
+ dprintf(STDERR_FILENO, " git-upload-archive <repo> - Archive repository\n");
return (EXIT_FAILURE);
}
- const char *user = get_git_user();
-
- if (user) {
- fputs("Authenticated as: ", stderr);
- fputs(user, stderr);
- fputs("\n", stderr);
+ if (slice_isempty(user)) {
+ dprintf(STDERR_FILENO, "Authenticated as: %.*s\n", (int32_t)user.size, user.ptr);
} else {
- fputs("Anonymous access\n", stderr);
+ dprintf(STDERR_FILENO, "Anonymous access\n");
}
- if (!check_permissions(cmd)) {
+ if (!check_permissions(&user, cmd)) {
return 1;
}
diff --git a/slice.c b/slice.c
index c3fcab1..9ce19c3 100644
--- a/slice.c
+++ b/slice.c
@@ -1,10 +1,11 @@
#include <unistd.h>
#include <string.h>
+#include <stdlib.h>
#include "./slice.h"
struct slice
-cstrtoslice(const char *s) {
+slice_fromcstr(const char *s) {
struct slice result;
result.ptr = s;
@@ -12,3 +13,33 @@ cstrtoslice(const char *s) {
return (result);
}
+
+int32_t
+slice_isempty(const struct slice s) {
+ if (0 == s.size) return (1);
+ return (0);
+}
+
+struct slice
+slice_empty(void) {
+ return (struct slice){
+ .ptr = 0x00,
+ .size = 0,
+ };
+}
+
+char *
+slice_tocstr(const struct slice s) {
+ if (0 == s.ptr || 0 == s.size) {
+ return 0x00;
+ }
+
+ char *str = malloc(s.size + 1);
+ if (!str) {
+ return 0x00;
+ }
+
+ memcpy(str, s.ptr, s.size);
+ str[s.size] = '\0';
+ return str;
+}
diff --git a/slice.h b/slice.h
index b064ace..725351e 100644
--- a/slice.h
+++ b/slice.h
@@ -9,7 +9,15 @@ struct slice {
};
struct slice
-cstrtoslice(const char *s);
+slice_fromcstr(const char *s);
+int32_t
+slice_isempty(const struct slice s);
+
+struct slice
+slice_empty(void);
+
+char *
+slice_tocstr(const struct slice s);
#endif