diff --git a/unix/kasmvncpasswd/CMakeLists.txt b/unix/kasmvncpasswd/CMakeLists.txt index 2edb0d7..600ee34 100644 --- a/unix/kasmvncpasswd/CMakeLists.txt +++ b/unix/kasmvncpasswd/CMakeLists.txt @@ -1,7 +1,8 @@ include_directories(${CMAKE_SOURCE_DIR}/common) add_executable(kasmvncpasswd - kasmvncpasswd.c) + kasmvncpasswd.c + kasmpasswd.c) target_link_libraries(kasmvncpasswd crypt) diff --git a/unix/kasmvncpasswd/kasmpasswd.c b/unix/kasmvncpasswd/kasmpasswd.c new file mode 100644 index 0000000..03e6dd8 --- /dev/null +++ b/unix/kasmvncpasswd/kasmpasswd.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include + +#include "kasmpasswd.h" + +struct kasmpasswd_t *readkasmpasswd(const char path[]) { + + struct kasmpasswd_t *set = calloc(sizeof(struct kasmpasswd_t), 1); + FILE *f = fopen(path, "r"); + if (!f) + return set; + + // Count lines + unsigned lines = 0; + char buf[4096]; + + while (fgets(buf, 4096, f)) { + lines++; + } + + rewind(f); + + set->entries = calloc(sizeof(struct kasmpasswd_entry_t), lines); + + unsigned cur = 0; + while (fgets(buf, 4096, f)) { + char *lim = strchr(buf, ':'); + if (!lim) + continue; + *lim = '\0'; + lim++; + + const char * const pw = lim; + + lim = strchr(lim, ':'); + if (!lim) + continue; + *lim = '\0'; + lim++; + + const char * const perms = lim; + + lim = strchr(lim, '\n'); + if (lim) + *lim = '\0'; + + if (strlen(buf) + 1 > sizeof(((struct kasmpasswd_entry_t *)0)->user)) { + fprintf(stderr, "Username %s too long\n", buf); + continue; + } + if (strlen(pw) + 1 > sizeof(((struct kasmpasswd_entry_t *)0)->password)) { + fprintf(stderr, "Password for user %s too long\n", buf); + continue; + } + + strcpy(set->entries[cur].user, buf); + strcpy(set->entries[cur].password, pw); + + if (strchr(perms, 'w')) + set->entries[cur].write = 1; + if (strchr(perms, 'o')) + set->entries[cur].owner = 1; + + cur++; + } + + fclose(f); + + set->num = cur; + + return set; +} + +void writekasmpasswd(const char path[], const struct kasmpasswd_t *set) { + char tmpname[PATH_MAX]; + + if (!set || !set->entries || !set->num) + return; + + snprintf(tmpname, PATH_MAX, "%s.tmp", path); + tmpname[PATH_MAX - 1] = '\0'; + + FILE *f = fopen(tmpname, "w"); + if (!f) { + fprintf(stderr, "Failed to open temp file %s\n", tmpname); + return; + } + + static const char * const perms[] = { + "", + "w", + "o", + "ow" + }; + + unsigned i; + for (i = 0; i < set->num; i++) { + fprintf(f, "%s:%s:%s\n", + set->entries[i].user, + set->entries[i].password, + perms[set->entries[i].owner * 2 + set->entries[i].write]); + } + + fsync(fileno(f)); + fclose(f); + + if (rename(tmpname, path)) + fprintf(stderr, "Failed writing the password file %s\n", path); + chmod(path, S_IRUSR|S_IWUSR); +} diff --git a/unix/kasmvncpasswd/kasmpasswd.h b/unix/kasmvncpasswd/kasmpasswd.h new file mode 100644 index 0000000..6bff018 --- /dev/null +++ b/unix/kasmvncpasswd/kasmpasswd.h @@ -0,0 +1,27 @@ +#ifndef KASMPASSWD_H +#define KASMPASSWD_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct kasmpasswd_entry_t { + char user[32]; + char password[128]; + unsigned char write : 1; + unsigned char owner : 1; +}; + +struct kasmpasswd_t { + struct kasmpasswd_entry_t *entries; + unsigned num; +}; + +struct kasmpasswd_t *readkasmpasswd(const char path[]); +void writekasmpasswd(const char path[], const struct kasmpasswd_t *set); + +#ifdef __cplusplus +} // extern C +#endif + +#endif diff --git a/unix/kasmvncpasswd/kasmvncpasswd.c b/unix/kasmvncpasswd/kasmvncpasswd.c index 6c41da4..3e47375 100644 --- a/unix/kasmvncpasswd/kasmvncpasswd.c +++ b/unix/kasmvncpasswd/kasmvncpasswd.c @@ -29,10 +29,16 @@ #include #include +#include "kasmpasswd.h" + static void usage(const char *prog) { - fprintf(stderr, "Usage: %s [file]\n", prog); - fprintf(stderr, " %s -f\n", prog); + fprintf(stderr, "Usage: %s -u username [-wno] [file]\n" + "-w Write permission\n" + "-o Owner\n" + "-n Don't change password, change permissions only\n" + "\n" + "The file is updated atomically.\n", prog); exit(1); } @@ -68,18 +74,6 @@ static char* getpassword(const char* prompt, char *buf) { static char pw1[4096]; static char pw2[4096]; -// Reads passwords from stdin and prints encrypted passwords to stdout. -static int encrypt_pipe() { - char *result = getpassword(NULL, pw1); - if (!result) - return 1; - - printf("%s", encryptpw(result)); - fflush(stdout); - - return 0; -} - static const char *readpassword() { while (1) { if (!getpassword("Password:", pw1)) { @@ -110,20 +104,43 @@ static const char *readpassword() { int main(int argc, char** argv) { - char* fname = 0; - - for (int i = 1; i < argc; i++) { - if (strncmp(argv[i], "-f", 2) == 0) { - return encrypt_pipe(); - } else if (argv[i][0] == '-') { - usage(argv[0]); - } else if (!fname) { - fname = argv[i]; - } else { - usage(argv[0]); + const char *fname = NULL; + const char *user = NULL; + const char args[] = "u:wno"; + int opt; + + unsigned char nopass = 0, writer = 0, owner = 0; + + while ((opt = getopt(argc, argv, args)) != -1) { + switch (opt) { + case 'u': + user = optarg; + if (strlen(user) + 1 > sizeof(((struct kasmpasswd_entry_t *)0)->user)) { + fprintf(stderr, "Username %s too long\n", user); + exit(1); + } + break; + case 'n': + nopass = 1; + break; + case 'w': + writer = 1; + break; + case 'o': + owner = 1; + break; + default: + usage(argv[0]); + break; } } + if (!user) + usage(argv[0]); + + if (optind < argc) + fname = argv[optind]; + if (!fname) { wordexp_t wexp; if (!wordexp("~/.kasmpasswd", &wexp, WRDE_NOCMD) && wexp.we_wordv[0]) @@ -133,23 +150,42 @@ int main(int argc, char** argv) if (!fname) usage(argv[0]); - while (1) { - const char *encrypted = readpassword(); + // Action + struct kasmpasswd_t *set = readkasmpasswd(fname); + unsigned i; - FILE* fp = fopen(fname, "w"); - if (!fp) { - fprintf(stderr, "Couldn't open %s for writing\n", fname); - exit(1); + if (nopass) { + for (i = 0; i < set->num; i++) { + if (!strcmp(set->entries[i].user, user)) { + set->entries[i].write = writer; + set->entries[i].owner = owner; + + writekasmpasswd(fname, set); + return 0; + } + } + fprintf(stderr, "No user named %s found\n", user); + return 1; + } else { + const char *encrypted = readpassword(); + for (i = 0; i < set->num; i++) { + if (!strcmp(set->entries[i].user, user)) + break; } - chmod(fname, S_IRUSR|S_IWUSR); - if (fwrite(encrypted, strlen(encrypted), 1, fp) != 1) { - fprintf(stderr,"Writing to %s failed\n",fname); - exit(1); + // No existing user by that name? + if (i >= set->num) { + i = set->num++; + set->entries = realloc(set->entries, set->num * sizeof(struct kasmpasswd_entry_t)); } - fclose(fp); + strcpy(set->entries[i].user, user); + strcpy(set->entries[i].password, encrypted); + set->entries[i].write = writer; + set->entries[i].owner = owner; - return 0; + writekasmpasswd(fname, set); } + + return 0; }