summaryrefslogtreecommitdiffstats
path: root/fluent-bit/lib/monkey/plugins/auth
diff options
context:
space:
mode:
Diffstat (limited to 'fluent-bit/lib/monkey/plugins/auth')
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/ABOUT2
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/CMakeLists.txt10
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/OPTIONAL0
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/auth.c254
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/auth.h106
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/base64.c172
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/base64.h11
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/conf.c244
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/conf.h25
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/sha1.c288
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/sha1.h22
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/tools/CMakeLists.txt15
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/tools/mk_passwd.c209
13 files changed, 1358 insertions, 0 deletions
diff --git a/fluent-bit/lib/monkey/plugins/auth/ABOUT b/fluent-bit/lib/monkey/plugins/auth/ABOUT
new file mode 100644
index 000000000..66a5384c3
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/ABOUT
@@ -0,0 +1,2 @@
+HTTP Basic Authentication
+=========================
diff --git a/fluent-bit/lib/monkey/plugins/auth/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/auth/CMakeLists.txt
new file mode 100644
index 000000000..a6fd27a06
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/CMakeLists.txt
@@ -0,0 +1,10 @@
+set(src
+ auth.c
+ base64.c
+ conf.c
+ sha1.c
+ )
+
+add_subdirectory(tools)
+
+MONKEY_PLUGIN(auth "${src}")
diff --git a/fluent-bit/lib/monkey/plugins/auth/OPTIONAL b/fluent-bit/lib/monkey/plugins/auth/OPTIONAL
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/OPTIONAL
diff --git a/fluent-bit/lib/monkey/plugins/auth/auth.c b/fluent-bit/lib/monkey/plugins/auth/auth.c
new file mode 100644
index 000000000..b0f983bfb
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/auth.c
@@ -0,0 +1,254 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/mk_api.h>
+
+#include <sys/stat.h>
+
+#include "auth.h"
+#include "conf.h"
+#include "sha1.h"
+#include "base64.h"
+
+static int mk_auth_validate_user(struct users_file *users,
+ const char *credentials, unsigned int len)
+{
+ int sep;
+ size_t auth_len;
+ unsigned char *decoded = NULL;
+ unsigned char digest[SHA1_DIGEST_LEN];
+ struct mk_list *head;
+ struct user *entry;
+
+ SHA_CTX sha; /* defined in sha1/sha1.h */
+
+ /* Validate value length */
+ if (len <= auth_header_basic.len + 1) {
+ return -1;
+ }
+
+ /* Validate 'basic' credential type */
+ if (strncmp(credentials, auth_header_basic.data,
+ auth_header_basic.len) != 0) {
+ return -1;
+ }
+
+ /* Decode credentials: incoming credentials comes in base64 encode */
+ decoded = base64_decode((unsigned char *) credentials + auth_header_basic.len,
+ len - auth_header_basic.len,
+ &auth_len);
+ if (decoded == NULL) {
+ PLUGIN_TRACE("Failed to decode credentials.");
+ goto error;
+ }
+
+ if (auth_len <= 3) {
+ goto error;
+ }
+
+ sep = mk_api->str_search_n((char *) decoded, ":", 1, auth_len);
+ if (sep == -1 || sep == 0 || (unsigned int) sep == auth_len - 1) {
+ goto error;
+ }
+
+ /* Get SHA1 hash */
+ SHA1_Init(&sha);
+ SHA1_Update(&sha, (unsigned char *) decoded + sep + 1, auth_len - (sep + 1));
+ SHA1_Final(digest, &sha);
+
+ mk_list_foreach(head, &users->_users) {
+ entry = mk_list_entry(head, struct user, _head);
+ /* match user */
+ if (strlen(entry->user) != (unsigned int) sep) {
+ continue;
+ }
+ if (strncmp(entry->user, (char *) decoded, sep) != 0) {
+ continue;
+ }
+
+ PLUGIN_TRACE("User match '%s'", entry->user);
+
+ /* match password */
+ if (memcmp(entry->passwd_decoded, digest, SHA1_DIGEST_LEN) == 0) {
+ PLUGIN_TRACE("User '%s' matched password", entry->user);
+ mk_api->mem_free(decoded);
+ return 0;
+ }
+ PLUGIN_TRACE("Invalid password");
+ break;
+ }
+
+ error:
+ if (decoded) {
+ mk_api->mem_free(decoded);
+ }
+ return -1;
+}
+
+int mk_auth_plugin_init(struct plugin_api **api, char *confdir)
+{
+ (void) confdir;
+
+ mk_api = *api;
+
+ /* Init and load global users list */
+ mk_list_init(&vhosts_list);
+ mk_list_init(&users_file_list);
+ mk_auth_conf_init_users_list();
+
+ /* Set HTTP headers key */
+ auth_header_basic.data = MK_AUTH_HEADER_BASIC;
+ auth_header_basic.len = sizeof(MK_AUTH_HEADER_BASIC) - 1;
+
+ return 0;
+}
+
+int mk_auth_plugin_exit()
+{
+ return 0;
+}
+
+void mk_auth_worker_init()
+{
+ char *user;
+
+ /* Init thread buffer for given credentials */
+ user = mk_api->mem_alloc(MK_AUTH_CREDENTIALS_LEN - 1);
+ pthread_setspecific(_mkp_data, (void *) user);
+}
+
+/* Object handler */
+int mk_auth_stage30(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr,
+ int n_params,
+ struct mk_list *params)
+{
+ int val;
+ short int is_restricted = MK_FALSE;
+ struct mk_list *vh_head;
+ struct mk_list *loc_head;
+ struct vhost *vh_entry = NULL;
+ struct location *loc_entry;
+ struct mk_http_header *header;
+ (void) plugin;
+ (void) n_params;
+ (void) params;
+
+ PLUGIN_TRACE("[FD %i] Handler received request");
+
+ /* Match auth_vhost with global vhost */
+ mk_list_foreach(vh_head, &vhosts_list) {
+ vh_entry = mk_list_entry(vh_head, struct vhost, _head);
+ if (vh_entry->host == sr->host_conf) {
+ PLUGIN_TRACE("[FD %i] host matched %s",
+ cs->socket,
+ mk_api->config->server_signature);
+ break;
+ }
+ }
+
+ if (!vh_entry) {
+ return MK_PLUGIN_RET_NOT_ME;
+ }
+
+ /* Check vhost locations */
+ mk_list_foreach(loc_head, &vh_entry->locations) {
+ loc_entry = mk_list_entry(loc_head, struct location, _head);
+ if (sr->uri_processed.len < loc_entry->path.len) {
+ continue;
+ }
+ if (strncmp(sr->uri_processed.data,
+ loc_entry->path.data, loc_entry->path.len) == 0) {
+ is_restricted = MK_TRUE;
+ PLUGIN_TRACE("[FD %i] Location matched %s",
+ cs->socket,
+ loc_entry->path.data);
+ break;
+ }
+ }
+
+ /* For non-restricted location do not take any action, just returns */
+ if (is_restricted == MK_FALSE) {
+ return MK_PLUGIN_RET_NOT_ME;
+ }
+
+ /* Check authorization header */
+ header = mk_api->header_get(MK_HEADER_AUTHORIZATION,
+ sr, NULL, 0);
+
+ if (header) {
+ /* Validate user */
+ val = mk_auth_validate_user(loc_entry->users,
+ header->val.data, header->val.len);
+ if (val == 0) {
+ /* user validated, success */
+ PLUGIN_TRACE("[FD %i] user validated!", cs->socket);
+ return MK_PLUGIN_RET_NOT_ME;
+ }
+ }
+
+ /* Restricted access: requires auth */
+ PLUGIN_TRACE("[FD %i] unauthorized user, credentials required",
+ cs->socket);
+
+ sr->headers.content_length = 0;
+ mk_api->header_set_http_status(sr, MK_CLIENT_UNAUTH);
+ mk_api->header_add(sr,
+ loc_entry->auth_http_header.data,
+ loc_entry->auth_http_header.len);
+
+ mk_api->header_prepare(plugin, cs, sr);
+ return MK_PLUGIN_RET_END;
+}
+
+int mk_auth_stage30_hangup(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr)
+{
+ (void) plugin;
+ (void) cs;
+ (void) sr;
+
+ return 0;
+}
+
+struct mk_plugin_stage mk_plugin_stage_auth = {
+ .stage30 = &mk_auth_stage30,
+ .stage30_hangup = &mk_auth_stage30_hangup
+};
+
+struct mk_plugin mk_plugin_auth = {
+ /* Identification */
+ .shortname = "auth",
+ .name = "Basic Authentication",
+ .version = MK_VERSION_STR,
+ .hooks = MK_PLUGIN_STAGE,
+
+ /* Init / Exit */
+ .init_plugin = mk_auth_plugin_init,
+ .exit_plugin = mk_auth_plugin_exit,
+
+ /* Init Levels */
+ .master_init = NULL,
+ .worker_init = mk_auth_worker_init,
+
+ /* Type */
+ .stage = &mk_plugin_stage_auth
+};
diff --git a/fluent-bit/lib/monkey/plugins/auth/auth.h b/fluent-bit/lib/monkey/plugins/auth/auth.h
new file mode 100644
index 000000000..f6eebe83e
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/auth.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MK_AUTH_H
+#define MK_AUTH_H
+
+#include <monkey/mk_api.h>
+
+/* Header stuff */
+#define MK_AUTH_HEADER_BASIC "Basic "
+#define MK_AUTH_HEADER_TITLE "WWW-Authenticate: Basic realm=\"%s\""
+
+/* Credentials length */
+#define MK_AUTH_CREDENTIALS_LEN 256
+
+/*
+ * The plugin hold one struct per virtual host and link to the
+ * locations and users file associated:
+ *
+ * +---------------------------------+
+ * struct vhost > vhost (1:N) |
+ * | +---------+----------+ |
+ * | | | | |
+ * struct location > location location location |
+ * | | | | |
+ * | +----+----+ + |
+ * | | | |
+ * struct users > users users |
+ * +---------------------------------+
+ *
+ */
+
+/* List of virtual hosts to handle locations */
+struct mk_list vhosts_list;
+
+/* main index for locations under a virtualhost */
+struct vhost {
+ struct mk_vhost *host;
+ struct mk_list locations;
+ struct mk_list _head;
+};
+
+/*
+ * A location restrict a filesystem path with a list
+ * of allowed users
+ */
+struct location {
+ mk_ptr_t path;
+ mk_ptr_t title;
+ mk_ptr_t auth_http_header;
+
+ struct users_file *users;
+ struct mk_list _head;
+};
+
+/* Head index for user files list */
+struct mk_list users_file_list;
+
+/*
+ * Represents a users file, each entry represents a physical
+ * file and belongs to a node of the users_file_list list
+ */
+struct users_file {
+ time_t last_updated; /* last time this entry was modified */
+ char *path; /* file path */
+ struct mk_list _users; /* list of users */
+ struct mk_list _head; /* head for main mk_list users_file_list */
+};
+
+/*
+ * a list of users, this list belongs to a
+ * struct location
+ */
+struct user {
+ char user[128];
+ char passwd_raw[256];
+ unsigned char *passwd_decoded;
+
+ struct mk_list _head;
+};
+
+struct mk_list users_file_list;
+
+/* Thread key */
+mk_ptr_t auth_header_request;
+mk_ptr_t auth_header_basic;
+
+#define SHA1_DIGEST_LEN 20
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/auth/base64.c b/fluent-bit/lib/monkey/plugins/auth/base64.c
new file mode 100644
index 000000000..e3149fe73
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/base64.c
@@ -0,0 +1,172 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifdef _FORCE_SYSMALLOC
+#undef MALLOC_JEMALLOC
+#endif
+
+#include <monkey/mk_api.h>
+#include "base64.h"
+
+#if defined(MALLOC_JEMALLOC)
+#define __mem_alloc mk_api->mem_alloc
+#define __mem_free mk_api->mem_free
+#else
+#define __mem_alloc malloc
+#define __mem_free free
+#endif
+
+static const unsigned char base64_table[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char *out, *pos;
+ const unsigned char *end, *in;
+ size_t olen;
+ int line_len;
+
+ olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
+ olen += olen / 72; /* line feeds */
+ olen++; /* nul termination */
+ if (olen < len)
+ return NULL; /* integer overflow */
+ if (mk_api != NULL) {
+ out = __mem_alloc(olen);
+ }
+ else {
+ out = __mem_alloc(olen);
+ }
+
+ if (out == NULL)
+ return NULL;
+
+ end = src + len;
+ in = src;
+ pos = out;
+ line_len = 0;
+ while (end - in >= 3) {
+ *pos++ = base64_table[in[0] >> 2];
+ *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+ *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+ *pos++ = base64_table[in[2] & 0x3f];
+ in += 3;
+ line_len += 4;
+ if (line_len >= 72) {
+ *pos++ = '\n';
+ line_len = 0;
+ }
+ }
+
+ if (end - in) {
+ *pos++ = base64_table[in[0] >> 2];
+ if (end - in == 1) {
+ *pos++ = base64_table[(in[0] & 0x03) << 4];
+ *pos++ = '=';
+ } else {
+ *pos++ = base64_table[((in[0] & 0x03) << 4) |
+ (in[1] >> 4)];
+ *pos++ = base64_table[(in[1] & 0x0f) << 2];
+ }
+ *pos++ = '=';
+ line_len += 4;
+ }
+
+ if (line_len)
+ *pos++ = '\n';
+
+ *pos = '\0';
+ if (out_len)
+ *out_len = pos - out;
+ return out;
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char dtable[256], *out, *pos, block[4], tmp;
+ size_t i, count, olen;
+ int pad = 0;
+
+ memset(dtable, 0x80, 256);
+ for (i = 0; i < sizeof(base64_table) - 1; i++)
+ dtable[base64_table[i]] = (unsigned char) i;
+ dtable['='] = 0;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ if (dtable[src[i]] != 0x80)
+ count++;
+ }
+
+ if (count == 0 || count % 4)
+ return NULL;
+
+ olen = (count / 4 * 3) + 1;
+ pos = out = __mem_alloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ tmp = dtable[src[i]];
+ if (tmp == 0x80)
+ continue;
+
+ if (src[i] == '=')
+ pad++;
+ block[count] = tmp;
+ count++;
+ if (count == 4) {
+ *pos++ = (block[0] << 2) | (block[1] >> 4);
+ *pos++ = (block[1] << 4) | (block[2] >> 2);
+ *pos++ = (block[2] << 6) | block[3];
+ count = 0;
+ if (pad) {
+ if (pad == 1)
+ pos--;
+ else if (pad == 2)
+ pos -= 2;
+ else {
+ /* Invalid padding */
+ __mem_free(out);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+ *pos = '\0';
+
+ *out_len = pos - out;
+ return out;
+}
diff --git a/fluent-bit/lib/monkey/plugins/auth/base64.h b/fluent-bit/lib/monkey/plugins/auth/base64.h
new file mode 100644
index 000000000..45001d47f
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/base64.h
@@ -0,0 +1,11 @@
+#ifndef AUTH_BASE64_H
+#define AUTH_BASE64_H
+
+
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len);
+unsigned char *base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len);
+
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/auth/conf.c b/fluent-bit/lib/monkey/plugins/auth/conf.c
new file mode 100644
index 000000000..6dede1dd4
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/conf.c
@@ -0,0 +1,244 @@
+ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <monkey/mk_api.h>
+#include "base64.h"
+#include "auth.h"
+#include "conf.h"
+
+/*
+ * Register a users file into the main list, if the users
+ * file already exists it just return the node in question,
+ * otherwise add the node to the list and return the node
+ * created.
+ */
+static struct users_file *mk_auth_conf_add_users(char *users_path)
+{
+ struct file_info finfo;
+ struct mk_list *head;
+ struct users_file *entry;
+ struct user *cred;
+ int i, sep, len;
+ int offset = 0;
+ size_t decoded_len;
+ char *buf;
+
+ mk_list_foreach(head, &users_file_list) {
+ entry = mk_list_entry(head, struct users_file, _head);
+ if (strcmp(entry->path, users_path) == 0) {
+ return entry;
+ }
+ }
+
+ if (mk_api->file_get_info(users_path, &finfo, MK_FILE_READ) != 0) {
+ mk_warn("Auth: Invalid users file '%s'", users_path);
+ return NULL;
+ }
+
+ if (finfo.is_directory == MK_TRUE) {
+ mk_warn("Auth: Not a credentials file '%s'", users_path);
+ return NULL;
+ }
+
+ if (finfo.read_access == MK_FALSE) {
+ mk_warn("Auth: Could not read file '%s'", users_path);
+ return NULL;
+ }
+
+ /* We did not find the path in our list, let's create a new node */
+ entry = mk_api->mem_alloc(sizeof(struct users_file));
+ entry->last_updated = finfo.last_modification;
+ entry->path = users_path;
+
+ /* Read file and add users to the list */
+ mk_list_init(&entry->_users);
+
+ /* Read credentials file */
+ buf = mk_api->file_to_buffer(users_path);
+ if (!buf) {
+ mk_warn("Auth: No users loaded '%s'", users_path);
+ return NULL;
+ }
+
+ /* Read users list buffer lines */
+ len = strlen(buf);
+ for (i = 0; i < len; i++) {
+ if (buf[i] == '\n' || (i) == len -1) {
+ sep = mk_api->str_search(buf + offset, ":", 1);
+
+ if (sep >= (int)sizeof(cred->user)) {
+ mk_warn("Auth: username too long");
+ offset = i + 1;
+ continue;
+ }
+ if (i - offset - sep - 1 - 5 >= (int)sizeof(cred->passwd_raw)) {
+ mk_warn("Auth: password hash too long");
+ offset = i + 1;
+ continue;
+ }
+
+ cred = mk_api->mem_alloc(sizeof(struct user));
+
+ /* Copy username */
+ strncpy(cred->user, buf + offset, sep);
+ cred->user[sep] = '\0';
+
+ /* Copy raw password */
+ offset += sep + 1 + 5;
+ strncpy(cred->passwd_raw,
+ buf + offset,
+ i - (offset));
+ cred->passwd_raw[i - offset] = '\0';
+
+ /* Decode raw password */
+ cred->passwd_decoded = base64_decode((unsigned char *)(cred->passwd_raw),
+ strlen(cred->passwd_raw),
+ &decoded_len);
+
+ offset = i + 1;
+
+ if (!cred->passwd_decoded) {
+ mk_warn("Auth: invalid user '%s' in '%s'",
+ cred->user, users_path);
+ mk_api->mem_free(cred);
+ continue;
+ }
+ mk_list_add(&cred->_head, &entry->_users);
+ }
+ }
+ mk_api->mem_free(buf);
+
+ /* Link node to global list */
+ mk_list_add(&entry->_head, &users_file_list);
+
+ return entry;
+}
+
+/*
+ * Read all vhost configuration nodes and looks for users files under an [AUTH]
+ * section, if present, it add that file to the unique list. It parse all user's
+ * files mentioned to avoid duplicated lists in memory.
+ */
+int mk_auth_conf_init_users_list()
+{
+ /* Section data */
+ char *location;
+ char *title;
+ char *users_path;
+ /* auth vhost list */
+ struct vhost *auth_vhost;
+
+ /* vhost configuration */
+ struct mk_list *head_hosts;
+ struct mk_list *hosts = &mk_api->config->hosts;
+ struct mk_list *head_sections;
+ struct mk_vhost *entry_host;
+ struct mk_rconf_section *section;
+
+ /* vhost [AUTH] locations */
+ struct location *loc;
+
+ /* User files list */
+ struct users_file *uf;
+
+ PLUGIN_TRACE("Loading user's files");
+
+ mk_list_foreach(head_hosts, hosts) {
+ entry_host = mk_list_entry(head_hosts, struct mk_vhost, _head);
+ if (!entry_host->config) {
+ continue;
+ }
+
+ auth_vhost = mk_api->mem_alloc(sizeof(struct vhost));
+ auth_vhost->host = entry_host; /* link virtual host entry */
+ mk_list_init(&auth_vhost->locations); /* init locations list */
+
+ /*
+ * check vhost 'config' and look for [AUTH] sections, we don't use
+ * mk_config_section_get() because we can have multiple [AUTH]
+ * sections.
+ */
+ mk_list_foreach(head_sections, &entry_host->config->sections) {
+ section = mk_list_entry(head_sections, struct mk_rconf_section, _head);
+
+ if (strcasecmp(section->name, "AUTH") == 0) {
+ location = NULL;
+ title = NULL;
+ users_path = NULL;
+
+ /* Get section keys */
+ location = mk_api->config_section_get_key(section,
+ "Location",
+ MK_RCONF_STR);
+ title = mk_api->config_section_get_key(section,
+ "Title",
+ MK_RCONF_STR);
+
+ users_path = mk_api->config_section_get_key(section,
+ "Users",
+ MK_RCONF_STR);
+
+ /* get or create users file entry */
+ uf = mk_auth_conf_add_users(users_path);
+ if (!uf) {
+ continue;
+ }
+
+ /* Location node */
+ loc = mk_api->mem_alloc(sizeof(struct location));
+ mk_api->pointer_set(&loc->path, location);
+ mk_api->pointer_set(&loc->title, title);
+
+ loc->auth_http_header.data = NULL;
+ mk_api->str_build(&loc->auth_http_header.data,
+ &loc->auth_http_header.len,
+ MK_AUTH_HEADER_TITLE, title);
+
+ loc->users = uf;
+
+ /* Add new location to auth_vhost node */
+ mk_list_add(&loc->_head, &auth_vhost->locations);
+ }
+ }
+
+ /* Link auth_vhost node to global list vhosts_list */
+ mk_list_add(&auth_vhost->_head, &vhosts_list);
+ }
+
+#ifdef TRACE
+ struct mk_list *vh_head, *loc_head;
+ struct vhost *vh_entry;
+ struct location *loc_entry;
+
+ mk_list_foreach(vh_head, &vhosts_list) {
+ vh_entry = mk_list_entry(vh_head, struct vhost, _head);
+ PLUGIN_TRACE("Auth VHost: %p", vh_entry->host);
+
+ mk_list_foreach(loc_head, &vh_entry->locations) {
+ loc_entry = mk_list_entry(loc_head, struct location, _head);
+ PLUGIN_TRACE("---");
+ PLUGIN_TRACE(" location: %s", loc_entry->path);
+ PLUGIN_TRACE(" title : %s", loc_entry->title);
+ PLUGIN_TRACE(" users : %s", loc_entry->users->path);
+ }
+ }
+#endif
+
+ return 0;
+}
diff --git a/fluent-bit/lib/monkey/plugins/auth/conf.h b/fluent-bit/lib/monkey/plugins/auth/conf.h
new file mode 100644
index 000000000..90fd8f79a
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/conf.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MK_AUTH_CONF_H
+#define MK_AUTH_CONF_H
+
+int mk_auth_conf_init_users_list();
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/auth/sha1.c b/fluent-bit/lib/monkey/plugins/auth/sha1.c
new file mode 100644
index 000000000..53c7946e8
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/sha1.c
@@ -0,0 +1,288 @@
+/*
+ * Code adapted to fill Monkey Project requirements, no big changes
+ * just a few header files added
+ */
+
+#include <arpa/inet.h>
+#include <string.h>
+
+/*
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
+ */
+
+#include "sha1.h"
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+/*
+ * Force usage of rol or ror by selecting the one with the smaller constant.
+ * It _can_ generate slightly smaller code (a constant of 1 is special), but
+ * perhaps more importantly it's possibly faster on any uarch that does a
+ * rotate with a loop.
+ */
+
+#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; })
+#define SHA_ROL(x,n) SHA_ASM("rol", x, n)
+#define SHA_ROR(x,n) SHA_ASM("ror", x, n)
+
+#else
+
+#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r)))
+#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n))
+#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n)
+
+#endif
+
+/*
+ * If you have 32 registers or more, the compiler can (and should)
+ * try to change the array[] accesses into registers. However, on
+ * machines with less than ~25 registers, that won't really work,
+ * and at least gcc will make an unholy mess of it.
+ *
+ * So to avoid that mess which just slows things down, we force
+ * the stores to memory to actually happen (we might be better off
+ * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as
+ * suggested by Artur Skawina - that will also make gcc unable to
+ * try to do the silly "optimize away loads" part because it won't
+ * see what the value will be).
+ *
+ * Ben Herrenschmidt reports that on PPC, the C version comes close
+ * to the optimized asm with this (ie on PPC you don't want that
+ * 'volatile', since there are lots of registers).
+ *
+ * On ARM we get the best code generation by forcing a full memory barrier
+ * between each SHA_ROUND, otherwise gcc happily get wild with spilling and
+ * the stack frame size simply explode and performance goes down the drain.
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+ #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
+#elif defined(__GNUC__) && defined(__arm__)
+ #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
+#else
+ #define setW(x, val) (W(x) = (val))
+#endif
+
+/*
+ * Performance might be improved if the CPU architecture is OK with
+ * unaligned 32-bit loads and a fast ntohl() is available.
+ * Otherwise fall back to byte loads and shifts which is portable,
+ * and is faster on architectures with memory alignment issues.
+ */
+
+#if defined(__i386__) || defined(__x86_64__) || \
+ defined(_M_IX86) || defined(_M_X64) || \
+ defined(__ppc__) || defined(__ppc64__) || \
+ defined(__powerpc__) || defined(__powerpc64__) || \
+ defined(__s390__) || defined(__s390x__)
+
+#define get_be32(p) ntohl(*(unsigned int *)(p))
+#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)
+
+#else
+
+#define get_be32(p) ( \
+ (*((unsigned char *)(p) + 0) << 24) | \
+ (*((unsigned char *)(p) + 1) << 16) | \
+ (*((unsigned char *)(p) + 2) << 8) | \
+ (*((unsigned char *)(p) + 3) << 0) )
+#define put_be32(p, v) do { \
+ unsigned int __v = (v); \
+ *((unsigned char *)(p) + 0) = __v >> 24; \
+ *((unsigned char *)(p) + 1) = __v >> 16; \
+ *((unsigned char *)(p) + 2) = __v >> 8; \
+ *((unsigned char *)(p) + 3) = __v >> 0; } while (0)
+
+#endif
+
+/* This "rolls" over the 512-bit array */
+#define W(x) (array[(x)&15])
+
+/*
+ * Where do we get the source from? The first 16 iterations get it from
+ * the input data, the next mix it from the 512-bit array.
+ */
+#define SHA_SRC(t) get_be32(data + t)
+#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)
+
+#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \
+ unsigned int TEMP = input(t); setW(t, TEMP); \
+ E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \
+ B = SHA_ROR(B, 2); } while (0)
+
+#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E )
+#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E )
+#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E )
+
+static void blk_SHA1_Block(blk_SHA_CTX *ctx, const unsigned int *data)
+{
+ unsigned int A,B,C,D,E;
+ unsigned int array[16];
+
+ A = ctx->H[0];
+ B = ctx->H[1];
+ C = ctx->H[2];
+ D = ctx->H[3];
+ E = ctx->H[4];
+
+ /* Round 1 - iterations 0-16 take their input from 'data' */
+ T_0_15( 0, A, B, C, D, E);
+ T_0_15( 1, E, A, B, C, D);
+ T_0_15( 2, D, E, A, B, C);
+ T_0_15( 3, C, D, E, A, B);
+ T_0_15( 4, B, C, D, E, A);
+ T_0_15( 5, A, B, C, D, E);
+ T_0_15( 6, E, A, B, C, D);
+ T_0_15( 7, D, E, A, B, C);
+ T_0_15( 8, C, D, E, A, B);
+ T_0_15( 9, B, C, D, E, A);
+ T_0_15(10, A, B, C, D, E);
+ T_0_15(11, E, A, B, C, D);
+ T_0_15(12, D, E, A, B, C);
+ T_0_15(13, C, D, E, A, B);
+ T_0_15(14, B, C, D, E, A);
+ T_0_15(15, A, B, C, D, E);
+
+ /* Round 1 - tail. Input from 512-bit mixing array */
+ T_16_19(16, E, A, B, C, D);
+ T_16_19(17, D, E, A, B, C);
+ T_16_19(18, C, D, E, A, B);
+ T_16_19(19, B, C, D, E, A);
+
+ /* Round 2 */
+ T_20_39(20, A, B, C, D, E);
+ T_20_39(21, E, A, B, C, D);
+ T_20_39(22, D, E, A, B, C);
+ T_20_39(23, C, D, E, A, B);
+ T_20_39(24, B, C, D, E, A);
+ T_20_39(25, A, B, C, D, E);
+ T_20_39(26, E, A, B, C, D);
+ T_20_39(27, D, E, A, B, C);
+ T_20_39(28, C, D, E, A, B);
+ T_20_39(29, B, C, D, E, A);
+ T_20_39(30, A, B, C, D, E);
+ T_20_39(31, E, A, B, C, D);
+ T_20_39(32, D, E, A, B, C);
+ T_20_39(33, C, D, E, A, B);
+ T_20_39(34, B, C, D, E, A);
+ T_20_39(35, A, B, C, D, E);
+ T_20_39(36, E, A, B, C, D);
+ T_20_39(37, D, E, A, B, C);
+ T_20_39(38, C, D, E, A, B);
+ T_20_39(39, B, C, D, E, A);
+
+ /* Round 3 */
+ T_40_59(40, A, B, C, D, E);
+ T_40_59(41, E, A, B, C, D);
+ T_40_59(42, D, E, A, B, C);
+ T_40_59(43, C, D, E, A, B);
+ T_40_59(44, B, C, D, E, A);
+ T_40_59(45, A, B, C, D, E);
+ T_40_59(46, E, A, B, C, D);
+ T_40_59(47, D, E, A, B, C);
+ T_40_59(48, C, D, E, A, B);
+ T_40_59(49, B, C, D, E, A);
+ T_40_59(50, A, B, C, D, E);
+ T_40_59(51, E, A, B, C, D);
+ T_40_59(52, D, E, A, B, C);
+ T_40_59(53, C, D, E, A, B);
+ T_40_59(54, B, C, D, E, A);
+ T_40_59(55, A, B, C, D, E);
+ T_40_59(56, E, A, B, C, D);
+ T_40_59(57, D, E, A, B, C);
+ T_40_59(58, C, D, E, A, B);
+ T_40_59(59, B, C, D, E, A);
+
+ /* Round 4 */
+ T_60_79(60, A, B, C, D, E);
+ T_60_79(61, E, A, B, C, D);
+ T_60_79(62, D, E, A, B, C);
+ T_60_79(63, C, D, E, A, B);
+ T_60_79(64, B, C, D, E, A);
+ T_60_79(65, A, B, C, D, E);
+ T_60_79(66, E, A, B, C, D);
+ T_60_79(67, D, E, A, B, C);
+ T_60_79(68, C, D, E, A, B);
+ T_60_79(69, B, C, D, E, A);
+ T_60_79(70, A, B, C, D, E);
+ T_60_79(71, E, A, B, C, D);
+ T_60_79(72, D, E, A, B, C);
+ T_60_79(73, C, D, E, A, B);
+ T_60_79(74, B, C, D, E, A);
+ T_60_79(75, A, B, C, D, E);
+ T_60_79(76, E, A, B, C, D);
+ T_60_79(77, D, E, A, B, C);
+ T_60_79(78, C, D, E, A, B);
+ T_60_79(79, B, C, D, E, A);
+
+ ctx->H[0] += A;
+ ctx->H[1] += B;
+ ctx->H[2] += C;
+ ctx->H[3] += D;
+ ctx->H[4] += E;
+}
+
+void blk_SHA1_Init(blk_SHA_CTX *ctx)
+{
+ ctx->size = 0;
+
+ /* Initialize H with the magic constants (see FIPS180 for constants) */
+ ctx->H[0] = 0x67452301;
+ ctx->H[1] = 0xefcdab89;
+ ctx->H[2] = 0x98badcfe;
+ ctx->H[3] = 0x10325476;
+ ctx->H[4] = 0xc3d2e1f0;
+}
+
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
+{
+ unsigned int lenW = ctx->size & 63;
+
+ ctx->size += len;
+
+ /* Read the data into W and process blocks as they get full */
+ if (lenW) {
+ unsigned int left = 64 - lenW;
+ if (len < left)
+ left = len;
+ memcpy(lenW + (char *)ctx->W, data, left);
+ lenW = (lenW + left) & 63;
+ len -= left;
+ data = ((const char *)data + left);
+ if (lenW)
+ return;
+ blk_SHA1_Block(ctx, ctx->W);
+ }
+ while (len >= 64) {
+ blk_SHA1_Block(ctx, data);
+ data = ((const char *)data + 64);
+ len -= 64;
+ }
+ if (len)
+ memcpy(ctx->W, data, len);
+}
+
+void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx)
+{
+ static const unsigned char pad[64] = { 0x80 };
+ unsigned int padlen[2];
+ int i;
+
+ /* Pad with a binary 1 (ie 0x80), then zeroes, then length */
+ padlen[0] = htonl((uint32_t)(ctx->size >> 29));
+ padlen[1] = htonl((uint32_t)(ctx->size << 3));
+
+ i = ctx->size & 63;
+ blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i)));
+ blk_SHA1_Update(ctx, padlen, 8);
+
+ /* Output hash */
+ for (i = 0; i < 5; i++)
+ put_be32(hashout + i*4, ctx->H[i]);
+}
diff --git a/fluent-bit/lib/monkey/plugins/auth/sha1.h b/fluent-bit/lib/monkey/plugins/auth/sha1.h
new file mode 100644
index 000000000..4a75ab351
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/sha1.h
@@ -0,0 +1,22 @@
+/*
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
+ */
+
+typedef struct {
+ unsigned long long size;
+ unsigned int H[5];
+ unsigned int W[16];
+} blk_SHA_CTX;
+
+void blk_SHA1_Init(blk_SHA_CTX *ctx);
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len);
+void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);
+
+#define SHA_CTX blk_SHA_CTX
+#define SHA1_Init blk_SHA1_Init
+#define SHA1_Update blk_SHA1_Update
+#define SHA1_Final blk_SHA1_Final
diff --git a/fluent-bit/lib/monkey/plugins/auth/tools/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/auth/tools/CMakeLists.txt
new file mode 100644
index 000000000..63b445cf7
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/tools/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(src
+ ../sha1.c
+ ../base64.c
+ mk_passwd.c
+ )
+
+include_directories(../)
+add_definitions(-D_FORCE_SYSMALLOC)
+add_executable(mk_passwd ${src})
+
+if(BUILD_LOCAL)
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/")
+else()
+ install(TARGETS mk_passwd RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_SBINDIR})
+endif()
diff --git a/fluent-bit/lib/monkey/plugins/auth/tools/mk_passwd.c b/fluent-bit/lib/monkey/plugins/auth/tools/mk_passwd.c
new file mode 100644
index 000000000..6dba1d3f1
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/tools/mk_passwd.c
@@ -0,0 +1,209 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifdef MALLOC_JEMALLOC
+#undef MALLOC_JEMALLOC
+#endif
+
+#include <monkey/monkey.h>
+#include <monkey/mk_core.h>
+
+#include <getopt.h>
+
+#include "sha1.h"
+#include "base64.h"
+
+#define MAX_LINE_LEN 256
+
+struct mk_passwd_user {
+ char *row;
+ struct mk_list _head;
+};
+
+/* Store a file as a linked list of its lines */
+static struct mk_list passwd_file;
+
+/* Load file to memory from disk
+ * If create_file == MK_TRUE, the file will be rewritten */
+void read_file(char *filename, int create_file)
+{
+ FILE *filein = fopen(filename, "r");
+ char line[MAX_LINE_LEN];
+ struct mk_passwd_user *entry;
+
+ mk_list_init(&passwd_file);
+
+ if (filein == NULL && create_file == MK_FALSE) {
+ printf("Error opening file %s\n", filename);
+ exit(1);
+ }
+
+ if (filein == NULL || create_file == MK_TRUE) {
+ if (filein != NULL)
+ fclose(filein);
+ return;
+ }
+
+ while (fgets(line, MAX_LINE_LEN, filein) != NULL) {
+ entry = malloc(sizeof(*entry));
+ entry->row = strdup(line);
+ mk_list_add(&entry->_head, &passwd_file);
+ }
+ fclose(filein);
+}
+
+/* Store data to disk */
+void dump_file(char *filename)
+{
+ FILE *fileout = fopen(filename, "w");
+ struct mk_list *it, *tmp;
+ struct mk_passwd_user *entry;
+
+ if (!fileout) {
+ printf("Error opening: %s", filename);
+ exit(EXIT_FAILURE);
+ }
+
+ mk_list_foreach_safe(it, tmp, &passwd_file) {
+ entry = mk_list_entry(it, struct mk_passwd_user, _head);
+ fprintf(fileout, "%s", entry->row);
+ mk_list_del(&entry->_head);
+ free(entry->row);
+ free(entry);
+ }
+ fclose(fileout);
+}
+
+/* Return sha1 hash of password
+ * A new line is appended at the hash */
+unsigned char *sha1_hash(const char *password)
+{
+
+ unsigned char sha_hash[20];
+ blk_SHA_CTX sha;
+
+ blk_SHA1_Init(&sha);
+ blk_SHA1_Update(&sha, password, strlen(password));
+ blk_SHA1_Final(sha_hash, &sha);
+
+ return base64_encode(sha_hash, 20, NULL);
+}
+
+void update_user(const char *username, const char *password, int create_user)
+{
+ struct mk_list *it, *tmp;
+ struct mk_passwd_user *entry;
+ unsigned char *hash_passwd;
+ int i;
+
+ mk_list_foreach_safe(it, tmp, &passwd_file) {
+ entry = mk_list_entry(it, struct mk_passwd_user, _head);
+ for (i = 0; entry->row[i] != '\0' && entry->row[i] != ':' && username[i] != '\0' && entry->row[i] == username[i]; i++);
+ if (entry->row[i] != ':' || username[i] != '\0')
+ continue;
+
+ /* Found a match */
+
+ /* Delete user */
+ if (create_user == MK_FALSE) {
+ printf("[-] Deleting user %s\n", username);
+ mk_list_del(&entry->_head);
+ free(entry->row);
+ free(entry);
+ return;
+ }
+
+ /* Update user */
+ printf("[+] Password changed for user %s\n", username);
+ hash_passwd = sha1_hash(password);
+ free(entry->row);
+ entry->row = malloc(512);
+ snprintf(entry->row, 512, "%s:{SHA1}%s", username, hash_passwd);
+ free(hash_passwd);
+
+ return;
+ }
+
+ /* Create user */
+ if (create_user == MK_TRUE) {
+ printf("[+] Adding user %s\n", username);
+ entry = malloc(sizeof(struct mk_passwd_user));
+ entry->row = malloc(512);
+ hash_passwd = sha1_hash(password);
+ snprintf(entry->row, 512, "%s:{SHA1}%s", username, hash_passwd);
+ free(hash_passwd);
+
+ mk_list_add(&entry->_head, &passwd_file);
+ }
+}
+
+static void print_help(int full_help)
+{
+ printf("Usage: mk_passwd [-c] [-D] filename username password\n");
+ if (full_help == MK_TRUE) {
+ printf("\nOptions:\n");
+ printf(" -h, --help\tshow this help message and exit\n");
+ printf(" -c\t\tCreate a new mkpasswd file, overwriting any existing file.\n");
+ printf(" -D\t\tRemove the given user from the password file.\n");
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int opt;
+ int create_user = MK_TRUE;
+ int create_file = MK_FALSE;
+ int show_help = MK_FALSE;
+ char *filename = NULL;
+ char *username = NULL;
+ char *password = NULL;
+
+ /* Command line options */
+ static const struct option long_opts[] = {
+ {"create", no_argument, NULL, 'c'},
+ {"delete_user", no_argument, NULL, 'D'},
+ {"help", no_argument, NULL, 'h'},
+ };
+
+ /* Parse options */
+ while ((opt = getopt_long(argc, argv, "hbDc", long_opts, NULL)) != -1) {
+ switch (opt) {
+ case 'c':
+ create_file = MK_TRUE;
+ break;
+ case 'D':
+ create_user = MK_FALSE;
+ break;
+ case 'h':
+ show_help = MK_TRUE;
+ break;
+ }
+ }
+
+ /* Retrieve filename, username and password */
+ while (optind < argc) {
+ if (filename == NULL)
+ filename = argv[optind++];
+ else if (username == NULL)
+ username = argv[optind++];
+ else if (password == NULL)
+ password = argv[optind++];
+ }
+
+ if (show_help == MK_TRUE) {
+ print_help(MK_TRUE);
+ exit(0);
+ }
+
+ /* If delete_user option is provided, do not provide a password */
+ if ((password != NULL) ^ (create_user == MK_TRUE)) {
+ print_help(MK_FALSE);
+ exit(1);
+ }
+
+ /* Process request */
+ read_file(filename, create_file);
+ update_user(username, password, create_user);
+ dump_file(filename);
+
+ return 0;
+}