summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Rühsen <tim.ruehsen@gmx.de>2019-05-25 21:19:47 +0200
committerTim Rühsen <tim.ruehsen@gmx.de>2019-05-29 17:01:45 +0200
commit6b2e17fcf2ba349875969e8e01fbeaf92afa59cf (patch)
treeb3e6abb7f96bb432966cda8650a3b7f65ab4fe40
parent98590fe57ba10372036f97fe0ff3c93220bca13c (diff)
downloadgnutls-tmp-remove-libopts.tar.gz
Add src/psk2.c using new src/options.c [skip ci]tmp-remove-libopts
Signed-off-by: Tim Rühsen <tim.ruehsen@gmx.de>
-rw-r--r--configure.ac2
-rw-r--r--doc/manpages/Makefile.am11
-rw-r--r--src/Makefile.am6
-rw-r--r--src/options.c428
-rw-r--r--src/options.h51
-rw-r--r--src/psk2.c334
-rw-r--r--src/psk2tool.md.in60
7 files changed, 890 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index 2112606601..2625231563 100644
--- a/configure.ac
+++ b/configure.ac
@@ -727,6 +727,7 @@ AM_MISSING_PROG([AUTOGEN], [autogen])
included_libopts=no
if test "$enable_tools" != "no" || test "$enable_doc" != "no"; then
+ AC_CHECK_PROGS([PANDOC], [pandoc])
AC_CHECK_PROGS([autogen], [autogen])
if test -z "$autogen"; then
@@ -747,6 +748,7 @@ else
gl_STDNORETURN_H
AM_CONDITIONAL([INSTALL_LIBOPTS],[false])
fi
+AM_CONDITIONAL([WITH_PANDOC], [ test -n "$PANDOC" ])
AM_CONDITIONAL(NEED_LIBOPTS, test "$included_libopts" = "yes")
diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am
index 9beee86060..5df18471d5 100644
--- a/doc/manpages/Makefile.am
+++ b/doc/manpages/Makefile.am
@@ -26,7 +26,7 @@ MAINTAINERCLEANFILES = stamp_mans
-include $(top_srcdir)/doc/doc.mk
TOOLS_MANS = gnutls-cli.1 gnutls-cli-debug.1 gnutls-serv.1 \
- certtool.1 psktool.1 p11tool.1 ocsptool.1 tpmtool.1
+ certtool.1 psktool.1 psk2tool.1 p11tool.1 ocsptool.1 tpmtool.1
SRP_MANS = srptool.1
DANE_MANS = danetool.1
@@ -99,6 +99,15 @@ psktool.1: ../../src/psktool-args.def
autogen -L ../../src -DMAN_SECTION=1 -Tagman-cmd.tpl "$<".tmp && \
rm -f "$<".tmp
+psk2tool.1: $(top_srcdir)/src/psk2.c $(top_srcdir)/src/psk2tool.md.in $(top_srcdir)/src/options.c
+ $(top_builddir)/src/psk2tool --options-md >$@.tmp
+ ( \
+ echo "% psk2tool(1) GnuTLS PSK2 tool|GnuTLS $(PACKAGE_VERSION)"; \
+ echo; \
+ sed -e '/@OPTIONS@/{r $@.tmp' -e 'd}' $(top_srcdir)/src/psk2tool.md.in; \
+ ) | $(PANDOC) -s -f markdown -t man -o $@
+ rm -f $@.tmp
+
APIMANS =
APIMANS += dane_cert_type_name.3
APIMANS += dane_cert_usage_name.3
diff --git a/src/Makefile.am b/src/Makefile.am
index 0374924c53..a40626206a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -82,7 +82,7 @@ else
LIBOPTS = $(LIBOPTS_LDADD)
endif
-bin_PROGRAMS = psktool gnutls-cli-debug certtool
+bin_PROGRAMS = psktool psk2tool gnutls-cli-debug certtool
if ENABLE_SRP
bin_PROGRAMS += srptool
endif
@@ -129,6 +129,10 @@ noinst_LTLIBRARIES += libcmd-psk.la
libcmd_psk_la_SOURCES = psktool-args.def
nodist_libcmd_psk_la_SOURCES = psktool-args.c psktool-args.h
+psk2tool_SOURCES = psk2.c options.c
+psk2tool_LDADD = ../lib/libgnutls.la ../gl/libgnu.la
+psk2tool_LDADD += $(LTLIBINTL) gl/libgnu_gpl.la
+
if ENABLE_OCSP
diff --git a/src/options.c b/src/options.c
new file mode 100644
index 0000000000..b50d4e3eb5
--- /dev/null
+++ b/src/options.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright(c) 2012 Tim Ruehsen
+ * Copyright(c) 2015-2019 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <c-ctype.h> // c_tolower, c_isalnum
+
+#include <gnutls/gnutls.h>
+#include "options.h"
+
+int parse_integer(option_t opt, const char *val, const char invert)
+{
+ *((int *)opt->var) = val ? atoi(val) : 0;
+
+ return 0;
+}
+
+static int parse_filename(option_t opt, const char *val, const char invert)
+{
+ free(*((char **)opt->var));
+// *((const char **)opt->var) = val ? shell_expand(val) : NULL;
+ *((char **)opt->var) = val ? strdup(val) : NULL;
+
+ return 0;
+}
+
+int parse_string(option_t opt, const char *val, const char invert)
+{
+ free(*((char **)opt->var));
+ *((char **)opt->var) = val ? strdup(val) : NULL;
+
+ return 0;
+}
+
+/*
+static int parse_stringset(option_t opt, const char *val, const char invert)
+{
+ wget_stringmap_t *map = *((wget_stringmap_t **)opt->var);
+
+ if (val) {
+ const char *s, *p;
+
+ wget_stringmap_clear(map);
+
+ for (s = p = val; *p; s = p + 1) {
+ if ((p = strchrnul(s, ',')) != s)
+ wget_stringmap_put_noalloc(map, wget_strmemdup(s, p - s), NULL);
+ }
+ } else {
+ wget_stringmap_clear(map);
+ }
+
+ return 0;
+}
+
+static const char *_strchrnul_esc(const char *s, char c)
+{
+ const char *p;
+
+ for (p = s; *p; p++) {
+ if (*p == '\\' && (p[1] == '\\' || p[1] == c))
+ p++;
+ else if (*p == c)
+ return p;
+ }
+
+ return p; // pointer to trailing \0
+}
+
+static char *_strmemdup_esc(const char *s, size_t size)
+{
+ const char *p, *e;
+ size_t newsize = 0;
+
+ for (p = s, e = s + size; p < e; p++) {
+ if (*p == '\\') {
+ if (p < e - 1) {
+ newsize++;
+ p++;
+ }
+ } else
+ newsize++;
+ }
+
+ char *ret = malloc(newsize + 1);
+ char *dst = ret;
+
+ for (p = s, e = s + size; p < e; p++) {
+ if (*p == '\\') {
+ if (p < e - 1)
+ *dst++ = *++p;
+ } else
+ *dst++ = *p;
+ }
+ *dst = 0;
+
+ return ret;
+}
+
+static int parse_stringlist_expand(option_t opt, const char *val, int expand, int max_entries)
+{
+ if (val && *val) {
+ wget_vector_t *v = *((wget_vector_t **)opt->var);
+ const char *s, *p;
+
+ if (!v)
+ v = *((wget_vector_t **)opt->var) = wget_vector_create(8, (wget_vector_compare_t)strcmp);
+
+ for (s = p = val; *p; s = p + 1) {
+ if ((p = _strchrnul_esc(s, ',')) != s) {
+ if (wget_vector_size(v) >= max_entries) {
+ wget_debug_printf("%s: More than %d entries, ignoring overflow\n", __func__, max_entries);
+ return -1;
+ }
+
+ const char *fname = _strmemdup_esc(s, p - s);
+
+ if (expand && *s == '~') {
+ wget_vector_add_noalloc(v, shell_expand(fname));
+ xfree(fname);
+ } else
+ wget_vector_add_noalloc(v, fname);
+ }
+ }
+ } else {
+ wget_vector_free(opt->var);
+ }
+
+ return 0;
+}
+
+static int parse_stringlist(option_t opt, const char *val, const char invert)
+{
+ // max number of 1024 entries to avoid out-of-memory
+ return parse_stringlist_expand(opt, val, 0, 1024);
+}
+*/
+
+int parse_bool(option_t opt, const char *val, const char invert)
+{
+ if (opt->var) {
+ if (!val || !strcmp(val, "1") || !strcmp(val, "y") || !strcmp(val, "yes") || !strcmp(val, "on"))
+ *((char *) opt->var) = !invert;
+ else if (!*val || !strcmp(val, "0") || !strcmp(val, "n") || !strcmp(val, "no") || !strcmp(val, "off"))
+ *((char *) opt->var) = invert;
+ else {
+ fprintf(stderr, "Invalid boolean value '%s'\n", val);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static inline void print_first(const char s, const char *l, const char *msg)
+{
+ if (s)
+ printf(" -%c, --%-20s %s", s, l, msg);
+ else
+ printf(" --%-20s %s", l, msg);
+}
+
+static inline void print_next(const char *msg)
+{
+ printf("%32s%s", "", msg);
+}
+
+void print_options(const struct optionw *options, int noptions)
+{
+ for (int it = 0; it < noptions; it++) {
+ print_first(options[it].short_name,
+ options[it].long_name,
+ options[it].help_str[0]);
+ for (unsigned i = 1; i < countof(options[it].help_str) && options[it].help_str[i]; i++)
+ print_next(options[it].help_str[i]);
+ }
+}
+
+static inline void print_first_md(const char s, const char *l, const char *msg)
+{
+ if (s)
+ printf("## `-%c`, `--%s`\n\n%s", s, l, msg);
+ else
+ printf("## `--%s`\n\n%s", l, msg);
+}
+
+static void print_options_md(const struct optionw *options, int noptions)
+{
+ for (int it = 0; it < noptions; it++) {
+ print_first_md(options[it].short_name,
+ options[it].long_name,
+ options[it].help_str[0]);
+ for (unsigned i = 1; i < countof(options[it].help_str) && options[it].help_str[i]; i++)
+ printf("%s", options[it].help_str[i]);
+ printf("\n");
+ }
+}
+
+static int opt_compare(const void *key, const void *option)
+{
+ return strcmp(key, ((option_t) option)->long_name);
+}
+
+static int opt_compare_config_linear(const char *key, const char *command)
+{
+ const char *s1 = key, *s2 = command;
+
+ for (; *s1 && *s2; s1++, s2++) {
+ if (*s2 == '-' || *s2 == '_') {
+ if (*s1 == '-' || *s1 == '_')
+ s1++;
+ s2++;
+ }
+
+ if (!*s1 || !*s2 || c_tolower(*s1) != *s2) break;
+ // *s2 is guaranteed to be lower case so convert *s1 to lower case
+ }
+
+ return *s1 != *s2; // no need for tolower() here
+}
+
+// return values:
+// < 0 : parse error
+// >= 0 : number of arguments processed
+static int set_long_option(const char *name, const char *value, const struct optionw *options, int noptions)
+{
+ option_t opt;
+ char invert = 0, value_present = 0, case_insensitive = 1;
+ char namebuf[strlen(name) + 1], *p;
+ int ret = 0, rc;
+
+ if ((p = strchr(name, '='))) {
+ // option with appended value
+ memcpy(namebuf, name, p - name);
+ namebuf[p - name] = 0;
+ name = namebuf;
+ value = p + 1;
+ value_present = 1;
+ }
+
+ // If the option is passed from .wget2rc (--*), delete the "--" prefix
+ if (!strncmp(name, "--", 2)) {
+ case_insensitive = 0;
+ name += 2;
+ }
+ // If the option is negated (--no-) delete the "no-" prefix
+ if (!strncmp(name, "no-", 3)) {
+ invert = 1;
+ name += 3;
+ }
+
+ if (case_insensitive) {
+ opt = bsearch(name, options, noptions, sizeof(options[0]), opt_compare);
+ if (!opt) {
+ // Fallback to linear search for 'unsharp' searching.
+ // Maybe the user asked for e.g. https_only or httpsonly instead of https-only
+ // opt_compare_config_linear() will find these. Wget -e/--execute compatibility.
+ for (int it = 0; it < noptions && !opt; it++)
+ if (opt_compare_config_linear(name, options[it].long_name) == 0)
+ opt = &options[it];
+ }
+ } else
+ opt = bsearch(name, options, noptions, sizeof(options[0]), opt_compare);
+
+ if (!opt) {
+ fprintf(stderr, "Unknown option '%s'\n", name);
+ return -1;
+ }
+
+ if (value_present) {
+ // "option=*"
+ if (invert) {
+ if (!opt->nargs || opt->parser == parse_string ||
+// opt->parser == parse_stringset ||
+// opt->parser == parse_stringlist ||
+ opt->parser == parse_filename)
+// || opt->parser == parse_filenames)
+ {
+ fprintf(stderr, "Option 'no-%s' doesn't allow an argument\n", name);
+ return -1;
+ }
+ } else if (!opt->nargs) {
+ printf("Option '%s' doesn't allow an argument\n", name);
+ return -1;
+ }
+ } else {
+ // "option"
+ switch (opt->nargs) {
+ case 0:
+ value = NULL;
+ break;
+ case 1:
+ if (!value) {
+ fprintf(stderr, "Missing argument for option '%s'\n", name);
+ // empty string is allowed in value i.e. *value = '\0'
+ return -1;
+ }
+
+ if (invert && (opt->parser == parse_string ||
+// opt->parser == parse_stringset ||
+// opt->parser == parse_stringlist ||
+ opt->parser == parse_filename))
+// || opt->parser == parse_filenames))
+ value = NULL;
+ else
+ ret = opt->nargs;
+ break;
+ case -1:
+ if(value)
+ ret = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((rc = opt->parser(opt, value, invert)) < 0)
+ return rc;
+
+ return ret;
+}
+
+int parse_command_line(int argc, const char **argv, const struct optionw *options, int noptions)
+{
+ static short shortcut_to_option[128];
+ const char *first_arg = NULL;
+ int n, rc;
+
+ if (argc == 2 && !strcmp(argv[1], "--options-md")) {
+ print_options_md(options, noptions);
+ exit(EXIT_SUCCESS);
+ }
+
+ // init the short option lookup table
+ if (!shortcut_to_option[0]) {
+ for (int it = 0; it < noptions; it++) {
+ if (options[it].short_name)
+ shortcut_to_option[(unsigned char)options[it].short_name] = it + 1;
+ }
+ }
+
+ // I like the idea of getopt() but not it's implementation (e.g. global variables).
+ // Therefore I implement my own getopt() behavior.
+ for (n = 1; n < argc && first_arg != argv[n]; n++) {
+ const char *argp = argv[n];
+
+ if (argp[0] != '-') {
+ // Move args behind options to allow mixed args/options like getopt().
+ // In the end, the order of the args is as before.
+ const char *cur = argv[n];
+ for (int it = n; it < argc - 1; it++)
+ argv[it] = argv[it + 1];
+ argv[argc - 1] = cur;
+
+ // Once we see the first arg again, we are done
+ if (!first_arg)
+ first_arg = cur;
+
+ n--;
+ continue;
+ }
+
+ if (argp[1] == '-') {
+ // long option
+ if (argp[2] == 0)
+ return n + 1;
+
+ if ((rc = set_long_option(argp + 2, n < argc - 1 ? argv[n+1] : NULL, options, noptions)) < 0)
+ return rc;
+
+ n += rc;
+
+ } else if (argp[1]) {
+ // short option(s)
+ for (int pos = 1; argp[pos]; pos++) {
+ option_t opt;
+ int idx;
+
+ if (c_isalnum(argp[pos]) && (idx = shortcut_to_option[(unsigned char)argp[pos]])) {
+ opt = &options[idx - 1];
+ // printf("opt=%p [%c]\n",(void *)opt,argp[pos]);
+ // printf("name=%s\n",opt->long_name);
+ if (opt->nargs > 0) {
+ const char *val;
+
+ if (!argp[pos + 1] && argc <= n + opt->nargs) {
+ fprintf(stderr,"Missing argument(s) for option '-%c'\n", argp[pos]);
+ return -1;
+ }
+ val = argp[pos + 1] ? argp + pos + 1 : argv[++n];
+ if ((rc = opt->parser(opt, val, 0)) < 0)
+ return rc;
+ n += rc;
+ break;
+ } else {//if (opt->args == 0)
+ if ((rc = opt->parser(opt, NULL, 0)) < 0)
+ return rc;
+ }
+ } else {
+ fprintf(stderr,"Unknown option '-%c'\n", argp[pos]);
+ return -1;
+ }
+ }
+ }
+ }
+
+ return n;
+}
diff --git a/src/options.h b/src/options.h
new file mode 100644
index 0000000000..033bd4d31d
--- /dev/null
+++ b/src/options.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GNUTLS_SRC_OPTIONS_H
+#define GNUTLS_SRC_OPTIONS_H
+
+// number of elements within an array
+#define countof(a) (sizeof(a)/sizeof(*(a)))
+
+typedef const struct optionw *option_t; // forward declaration
+
+struct optionw {
+ const char
+ *long_name;
+ void
+ *var;
+ int
+ (*parser)(option_t opt, const char *val, const char invert);
+ int
+ nargs; // -1: optional argument, else: number of arguments
+ char
+ short_name;
+ const char
+ *help_str[4];
+};
+
+int parse_command_line(int argc, const char **argv, const struct optionw *options, int noptions);
+int parse_integer(option_t opt, const char *val, const char invert);
+int parse_bool(option_t opt, const char *val, const char invert);
+int parse_string(option_t opt, const char *val, const char invert);
+void print_options(const struct optionw *options, int noptions);
+int print_version(option_t opt, const char *val, const char invert);
+int print_help(option_t opt, const char *val, const char invert);
+
+#endif /* GNUTLS_SRC_OPTIONS_H */
diff --git a/src/psk2.c b/src/psk2.c
new file mode 100644
index 0000000000..82184db4a0
--- /dev/null
+++ b/src/psk2.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2005-2012 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+/* Gnulib portability files. */
+
+#ifndef ENABLE_PSK
+
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ printf("\nPSK not supported. This program is a dummy.\n\n");
+ return 1;
+};
+
+#else
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef _WIN32
+#include <pwd.h>
+#include <unistd.h>
+#else
+#include <windows.h>
+#endif
+
+/* Gnulib portability files. */
+#include <minmax.h>
+#include "getpass.h"
+
+/* GnuTLS files */
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h> /* for random */
+#include "options.h"
+
+static struct config {
+ bool debug;
+ int keysize;
+ char *username;
+ char *pskfile;
+} config = {
+ // default values for config options (if not 0 or NULL)
+ .keysize = -1,
+};
+
+static const struct optionw options[] = {
+ { "debug", &config.debug, parse_bool, -1, 'd',
+ { "Print debugging messages. (default: off)\n"
+ }
+ },
+ { "help", NULL, print_help, 0, 'h',
+ { "Display extended usage information and exit\n"
+ }
+ },
+ { "pskfile", &config.pskfile, parse_string, 1, 'p',
+ { "Specify a pre-shared key file\n"
+ }
+ },
+ { "keysize", &config.keysize, parse_integer, 1, 's',
+ { "Specify the key size in bytes (default is 32-bytes or 256-bits)\n",
+ "- it must be in the range:\n",
+ "0 to 512\n"
+ }
+ },
+ { "username", &config.pskfile, parse_string, 1, 'u',
+ { "Specify the username to use\n"
+ }
+ },
+ { "version", NULL, print_version, -1, 'v',
+ { "Display the version information and exit\n"
+ }
+ },
+};
+
+int print_version(option_t opt, const char *val, const char invert)
+{
+ puts(
+ "psktool " PACKAGE_VERSION "\n"
+ "Copyright (C) 2000-2018 Free Software Foundation, and others, all rights reserved.\n"
+ "This is free software. It is licensed for use, modification and\n"
+ "redistribution under the terms of the GNU General Public License,\n"
+ "version 3 or later <https://gnu.org/licenses/gpl.html>\n"
+ "\n"
+ "Please send bug reports to: <bugs@gnutls.org>");
+
+ return -1; // stop processing & exit
+}
+
+int print_help(option_t opt, const char *val, const char invert)
+{
+ puts(
+ "psktool - GnuTLS PSK tool\n"
+ "Usage: psktool [ -<flag> [<val>] | --<name>[{=| }<val>] ]...\n");
+
+ print_options(options, countof(options));
+
+ puts(
+ "\n"
+ "Program that generates random keys for use with TLS-PSK. The keys are\n"
+ "stored in hexadecimal format in a key file.\n"
+ "\n"
+ "Please send bug reports to: <bugs@gnutls.org>");
+
+ return -1; // stop processing and exit
+}
+
+
+static int write_key(const char *username, const char *key, int key_size,
+ const char *passwd_file);
+
+#define MAX_KEY_SIZE 512
+int main(int argc, const char **argv)
+{
+ int ret;
+#ifndef _WIN32
+ struct passwd *pwd;
+#endif
+ unsigned char key[MAX_KEY_SIZE];
+ char hex_key[MAX_KEY_SIZE * 2 + 1];
+ int key_size;
+ gnutls_datum_t dkey;
+ const char *passwd, *username;
+ size_t hex_key_size = sizeof(hex_key);
+
+ if ((ret = gnutls_global_init()) < 0) {
+ fprintf(stderr, "global_init: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+
+ umask(066);
+
+ if (parse_command_line(argc, argv, options, countof(options)) < 0)
+ exit(1);
+
+ if (!config.pskfile) {
+ fprintf(stderr, "You need to specify a PSK key file\n");
+ exit(1);
+ } else
+ passwd = config.pskfile;
+
+ if (!config.username) {
+#ifndef _WIN32
+ pwd = getpwuid(getuid());
+
+ if (pwd == NULL) {
+ fprintf(stderr, "No such user\n");
+ return -1;
+ }
+
+ username = pwd->pw_name;
+#else
+ fprintf(stderr, "Please specify a user\n");
+ return -1;
+#endif
+ } else
+ username = config.username;
+
+ if (config.keysize > MAX_KEY_SIZE) {
+ fprintf(stderr, "Key size is too long\n");
+ exit(1);
+ }
+
+ if (config.keysize < 1)
+ key_size = 32;
+ else
+ key_size = config.keysize;
+
+ printf("Generating a random key for user '%s'\n", username);
+
+ ret = gnutls_rnd(GNUTLS_RND_RANDOM, (char *) key, key_size);
+ if (ret < 0) {
+ fprintf(stderr, "Not enough randomness\n");
+ exit(1);
+ }
+
+ dkey.data = key;
+ dkey.size = key_size;
+
+ ret = gnutls_hex_encode(&dkey, hex_key, &hex_key_size);
+ if (ret < 0) {
+ fprintf(stderr, "HEX encoding error\n");
+ exit(1);
+ }
+
+ ret = write_key(username, hex_key, hex_key_size, passwd);
+ if (ret == 0)
+ printf("Key stored to %s\n", passwd);
+
+ return ret;
+}
+
+static int filecopy(const char *src, const char *dst)
+{
+ FILE *fd, *fd2;
+ char line[5 * 1024];
+ char *p;
+
+ fd = fopen(dst, "w");
+ if (fd == NULL) {
+ fprintf(stderr, "Cannot open '%s' for write\n", dst);
+ return -1;
+ }
+
+ fd2 = fopen(src, "r");
+ if (fd2 == NULL) {
+ /* empty file */
+ fclose(fd);
+ return 0;
+ }
+
+ line[sizeof(line) - 1] = 0;
+ do {
+ p = fgets(line, sizeof(line) - 1, fd2);
+ if (p == NULL)
+ break;
+
+ fputs(line, fd);
+ }
+ while (1);
+
+ fclose(fd);
+ fclose(fd2);
+
+ return 0;
+}
+
+static int
+write_key(const char *username, const char *key, int key_size,
+ const char *passwd_file)
+{
+ FILE *fd;
+ char line[5 * 1024];
+ char *p, *pp;
+ char tmpname[1024];
+
+
+ /* delete previous entry */
+ struct stat st;
+ FILE *fd2;
+ int put;
+
+ if (strlen(passwd_file) + 5 > sizeof(tmpname)) {
+ fprintf(stderr, "file '%s' is tooooo long\n", passwd_file);
+ return -1;
+ }
+
+ snprintf(tmpname, sizeof(tmpname), "%s.tmp", passwd_file);
+
+ if (stat(tmpname, &st) != -1) {
+ fprintf(stderr, "file '%s' is locked\n", tmpname);
+ return -1;
+ }
+
+ if (filecopy(passwd_file, tmpname) != 0) {
+ fprintf(stderr, "Cannot copy '%s' to '%s'\n", passwd_file,
+ tmpname);
+ return -1;
+ }
+
+ fd = fopen(passwd_file, "w");
+ if (fd == NULL) {
+ fprintf(stderr, "Cannot open '%s' for write\n",
+ passwd_file);
+ (void)remove(tmpname);
+ return -1;
+ }
+
+ fd2 = fopen(tmpname, "r");
+ if (fd2 == NULL) {
+ fprintf(stderr, "Cannot open '%s' for read\n", tmpname);
+ (void)remove(tmpname);
+ fclose(fd);
+ return -1;
+ }
+
+ put = 0;
+ do {
+ p = fgets(line, sizeof(line) - 1, fd2);
+ if (p == NULL)
+ break;
+
+ pp = strchr(line, ':');
+ if (pp == NULL)
+ continue;
+
+ if (strncmp(p, username,
+ MAX(strlen(username),
+ (unsigned int) (pp - p))) == 0) {
+ put = 1;
+ fprintf(fd, "%s:%s\n", username, key);
+ } else {
+ fputs(line, fd);
+ }
+ }
+ while (1);
+
+ if (put == 0) {
+ fprintf(fd, "%s:%s\n", username, key);
+ }
+
+ fclose(fd);
+ fclose(fd2);
+
+ (void)remove(tmpname);
+
+
+ return 0;
+}
+
+#endif /* ENABLE_PSK */
diff --git a/src/psk2tool.md.in b/src/psk2tool.md.in
new file mode 100644
index 0000000000..3c2b4317ca
--- /dev/null
+++ b/src/psk2tool.md.in
@@ -0,0 +1,60 @@
+# NAME
+
+ psktool - GnuTLS PSK tool
+
+# SYNOPSIS
+
+ psktool [-flags] [-flag [value]] [--option-name[[=| ]value]]
+
+ All arguments must be options.
+
+# DESCRIPTION
+
+ Program that generates random keys for use with TLS-PSK. The keys are stored in hexadecimal format in a key
+ file.
+
+# OPTIONS
+
+@OPTIONS@
+
+# EXAMPLES
+
+To add a user 'psk_identity' in keys.psk for use with GnuTLS run:
+```
+$ ./psktool -u psk_identity -p keys.psk
+Generating a random key for user 'psk_identity'
+Key stored to keys.psk
+$ cat keys.psk
+psk_identity:88f3824b3e5659f52d00e959bacab954b6540344
+```
+
+This command will create keys.psk if it does not exist and will add user 'psk_identity'.
+
+# EXIT STATUS
+
+One of the following exit values will be returned:
+
+## 0 (EXIT_SUCCESS)
+
+ Successful program execution.
+
+## 1 (EXIT_FAILURE)
+
+ The operation failed or the command syntax was not valid.
+
+# SEE ALSO
+
+gnutls-cli-debug (1), gnutls-serv (1), srptool (1), certtool (1)
+
+# AUTHORS
+
+Nikos Mavrogiannopoulos, Simon Josefsson and others; see /usr/share/doc/gnutls/AUTHORS for a complete list.
+
+# COPYRIGHT
+
+Copyright (C) 2000-2019 Free Software Foundation, and others all rights reserved. This program is released
+under the terms of the GNU General Public License, version 3 or later.
+
+# BUGS
+
+Please send bug reports to: bugs@gnutls.org