From 4312f59942d91d06515e69e9d2686156feb1eaa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Nessj=C3=B8en?= Date: Sat, 3 Aug 2024 19:27:33 +0200 Subject: [PATCH] Add support for hashed passwords on server --- .gitignore | 1 + config/mactelnetd.users | 8 +- configure.ac | 14 ++ doc/Makefile.am | 5 + doc/{mactelnetd.1 => mactelnetd.1.in} | 24 ++- po/bg.po | 86 ++++----- po/nb.po | 86 ++++----- src/Makefile.am | 4 + src/mactelnet.c | 18 +- src/mactelnetd.c | 223 ++++++++++++++++++---- src/users.c | 254 +++++++++++++++++++++++++- src/users.h | 13 +- 12 files changed, 602 insertions(+), 134 deletions(-) rename doc/{mactelnetd.1 => mactelnetd.1.in} (55%) diff --git a/.gitignore b/.gitignore index 37b5a59..1fb9f52 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ src/mactelnetd src/mndp src/.deps/ src/config.h +doc/mactelnetd.1 \ No newline at end of file diff --git a/config/mactelnetd.users b/config/mactelnetd.users index c140e36..4c1594b 100644 --- a/config/mactelnetd.users +++ b/config/mactelnetd.users @@ -1,12 +1,12 @@ # Users file for MAC-Telnetd # #################################################################### -# WARNING: This file has passwords written in plain-text. # +# WARNING: This file may have passwords written in plain-text. # # Make sure this file is owned and only readable by root. # #################################################################### # -# Each line consists of a username and a password seperated by :. -# Usernames must be existing users from passwd. +# Each line consists username, hash, and salt seperated by :. +# If you need to add a new user, use the -a option in mactelnetd. # # Format: -#username:password +#username:hash:salt or username:password \ No newline at end of file diff --git a/configure.ac b/configure.ac index 5dc5dbd..cf9aad3 100644 --- a/configure.ac +++ b/configure.ac @@ -66,6 +66,20 @@ AM_CONDITIONAL([BUILD_MACTELNETD], [test x"$enable_mactelnetd" != "xno"]) # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h fcntl.h sys/random.h float.h libintl.h locale.h linux/netlink.h netinet/in.h paths.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h syslog.h termios.h unistd.h utmp.h utmpx.h]) +dnl check for readpassphrase. If none is found, we use getpass (with a warning) +AC_CHECK_HEADER([readpassphrase.h], + [READPASSPHRASE=native], + AC_CHECK_HEADER([bsd/readpassphrase.h], + [READPASSPHRASE=bsd], + [AC_MSG_WARN([falling back to obsoleted getpass(3)])])) + +AS_IF([test "x$READPASSPHRASE" = "xnative"],[ + AC_DEFINE([HAVE_READPASSPHRASE], [1], [Enable readpassphrase])]) + +AS_IF([test "x$READPASSPHRASE" = "xbsd"],[ + AC_DEFINE([HAVE_BSDREADPASSPHRASE], [1], [Enable bsdreadpassphrase]) + AC_SEARCH_LIBS([readpassphrase], [bsd], [], [AC_MSG_ERROR([library for bsd/readpassphrase.h not found])])]) + # Check if the target platform is macOS case "$host_os" in darwin*) diff --git a/doc/Makefile.am b/doc/Makefile.am index ddd81b8..b559e3b 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -2,4 +2,9 @@ dist_man_MANS = mactelnet.1 mndp.1 macping.1 if BUILD_MACTELNETD dist_man_MANS += mactelnetd.1 +CLEANFILES = mactelnetd.1 + +mactelnetd.1: mactelnetd.1.in + $(AM_V_GEN)$(SED) -e 's|@sysconfdir[@]|$(sysconfdir)|g' mactelnetd.1.in > $@ + endif \ No newline at end of file diff --git a/doc/mactelnetd.1 b/doc/mactelnetd.1.in similarity index 55% rename from doc/mactelnetd.1 rename to doc/mactelnetd.1.in index 40f9951..581666b 100644 --- a/doc/mactelnetd.1 +++ b/doc/mactelnetd.1.in @@ -16,6 +16,28 @@ Do not use broadcast packets. A tad less insecure. This means that ethernet packets will have the mac-address of the client as the packet destination, instead of using the ethernet broadcast address. .TP +.B \-o +Use the older MD5 based authentication. This is less secure, and also requires your userfile to have the passwords in plaintext format. If you are running the server with this parameter, you cannot add users using the +.B \-a +option. +.TP +.B \-a +Add a new user. The user should be an existing user in your system. You will be prompted for the username and password, or you can use one of the following options to specify them on the command line: +.RS +.TP +.B \-u \fIusername\fR +You can specify the new username to add on the command line using this option. If this is not used, you will be prompted for the username. +.TP +.B \-p \fIpassword\fR +You can specify the new password for the new user to add on the command line using this option. If this is not used, you will be prompted for the password. +.RE +.TP +.B \-d \fIusername\fR +Delete the specified user. +.TP +.B \-l +List the available users in the \fI@sysconfdir@/mactelnetd.users\fR file. +.TP .B \-h Show summary of options. .TP @@ -23,7 +45,7 @@ Show summary of options. Show version of program. .SH FILES .TP -.B /etc/mactelnetd.users +.B @sysconfdir@/mactelnetd.users This file contains a line separated list of users that will have access to your machine. Usernames and passwords are separated by colon. This file is read each time a user connects. diff --git a/po/bg.po b/po/bg.po index 00169e6..c5fe8cf 100644 --- a/po/bg.po +++ b/po/bg.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: mactelnet\n" "Report-Msgid-Bugs-To: haakon.nessjoen@gmail.com\n" -"POT-Creation-Date: 2024-04-22 21:15+0200\n" +"POT-Creation-Date: 2024-07-30 10:37+0200\n" "PO-Revision-Date: 2024-04-22 19:42+0200\n" "Last-Translator: Chat GPT \n" "Language-Team: Boian Bonev \n" @@ -177,7 +177,7 @@ msgstr "Парола: " msgid "Connecting to %s..." msgstr "Свързване към %s..." -#: src/mactelnet.c:762 src/mactelnetd.c:258 src/mactelnetd.c:1163 +#: src/mactelnet.c:762 src/mactelnetd.c:259 src/mactelnetd.c:1179 #, c-format msgid "Error binding to %s:%d, %s\n" msgstr "Грешка при свързване от %s:%d, %s\n" @@ -197,111 +197,111 @@ msgstr "готово\n" msgid "Username too long\n" msgstr "Потребителското име е твърде дълго\n" -#: src/mactelnetd.c:260 +#: src/mactelnetd.c:261 #, c-format msgid "Error binding to %s:%d on %s\n" msgstr "Грешка при свързване към %s:%d на %s\n" -#: src/mactelnetd.c:264 +#: src/mactelnetd.c:265 #, c-format msgid "Using %s to transmit packets from %s\n" msgstr "Използване на %s за предаване на пакети от %s\n" -#: src/mactelnetd.c:469 +#: src/mactelnetd.c:473 #, c-format msgid "(%d) Invalid login by %s." msgstr "(%d) Неправилен вход от %s." -#: src/mactelnetd.c:472 +#: src/mactelnetd.c:476 msgid "Login failed, incorrect username or password\r\n" msgstr "Неуспешен вход, невалиден потребител или парола\r\n" -#: src/mactelnetd.c:489 +#: src/mactelnetd.c:493 msgid "Terminal error\r\n" msgstr "Грешка в терминала\r\n" -#: src/mactelnetd.c:503 src/mactelnetd.c:511 +#: src/mactelnetd.c:507 src/mactelnetd.c:515 #, c-format msgid "(%d) Error allocating memory." msgstr "(%d) Грешка при заделяне на памет." -#: src/mactelnetd.c:505 src/mactelnetd.c:513 +#: src/mactelnetd.c:509 src/mactelnetd.c:517 msgid "System error, out of memory\r\n" msgstr "Системна грешка, недостатъчно памет\r\n" -#: src/mactelnetd.c:518 +#: src/mactelnetd.c:522 #, c-format msgid "(%d) Login ok, but local user not accessible (%s)." msgstr "(%d) Успешен вход, но локалният потребител не е достъпен (%s)." -#: src/mactelnetd.c:521 +#: src/mactelnetd.c:525 msgid "Local user not accessible\r\n" msgstr "Локалният потребител не е достъпен\r\n" -#: src/mactelnetd.c:532 +#: src/mactelnetd.c:536 #, c-format msgid "Error opening %s: %s" msgstr "Грешка при отваряне %s: %s" -#: src/mactelnetd.c:534 +#: src/mactelnetd.c:538 msgid "Error opening terminal\r\n" msgstr "Грешка при отваряне на терминал\r\n" -#: src/mactelnetd.c:545 +#: src/mactelnetd.c:549 #, c-format msgid "(%d) User %s logged in." msgstr "(%d) Потребител %s влезе." -#: src/mactelnetd.c:580 +#: src/mactelnetd.c:584 #, c-format msgid "(%d) Could not log in %s (%d:%d): setuid/setgid: %s" msgstr "(%d) Неуспешен вход %s (%d:%d): setuid/setgid: %s" -#: src/mactelnetd.c:583 +#: src/mactelnetd.c:587 msgid "Internal error\r\n" msgstr "Вътрешна грешка\r\n" -#: src/mactelnetd.c:589 +#: src/mactelnetd.c:593 #, c-format msgid "(%d) User %s disconnected with " msgstr "(%d) Потребител %s е изхвърлен с " -#: src/mactelnetd.c:715 +#: src/mactelnetd.c:731 #, c-format msgid "(%d) Invalid mtwei key by %s." msgstr "(%d) Невалиден ключ mtwei от %s." -#: src/mactelnetd.c:738 +#: src/mactelnetd.c:754 #, c-format msgid "(%d) Unhandeled control packet type: %d, length: %d" msgstr "(%d) Необработен тип на контролен пакет: %d, дължина: %d" -#: src/mactelnetd.c:742 +#: src/mactelnetd.c:758 #, c-format msgid "(%d) Unhandeled control packet type: %d, in state: %d, length: %d" msgstr "" "(%d) Необработен тип на контролен пакет: %d, в състояние: %d, дължина: %d" -#: src/mactelnetd.c:798 +#: src/mactelnetd.c:814 #, c-format msgid "(%d) New connection from %s." msgstr "(%d) Нова връзка от %s." -#: src/mactelnetd.c:827 src/mactelnetd.c:1318 +#: src/mactelnetd.c:843 src/mactelnetd.c:1334 #, c-format msgid "(%d) Connection closed." msgstr "(%d) Връзката е затворена." -#: src/mactelnetd.c:882 +#: src/mactelnetd.c:898 #, c-format msgid "(%d) Unhandeled packet type: %d" msgstr "(%d) Необработваем пакет от тип: %d" -#: src/mactelnetd.c:957 +#: src/mactelnetd.c:973 msgid "Was not able to send any MNDP packets" msgstr "Не успях да изпратя нито един MNDP пакет" -#: src/mactelnetd.c:966 +#: src/mactelnetd.c:982 msgid "" "\r\n" "\r\n" @@ -311,29 +311,29 @@ msgstr "" "\r\n" "Демон процеса прекратява работата си.\r\n" -#: src/mactelnetd.c:968 +#: src/mactelnetd.c:984 msgid "Daemon shutting down" msgstr "Демон процеса прекратява работата си" -#: src/mactelnetd.c:1001 +#: src/mactelnetd.c:1017 msgid "SIGHUP: Reloading interfaces" msgstr "SIGHUP: Презареждане на интерфейсите" -#: src/mactelnetd.c:1016 +#: src/mactelnetd.c:1032 msgid "No devices found! Exiting.\n" msgstr "Няма намерени устройства! Изход.\n" -#: src/mactelnetd.c:1030 +#: src/mactelnetd.c:1046 #, c-format msgid "(%d) Connection closed because interface %s is gone." msgstr "(%d) Връзката е затворена поради изчезнал интерфейс %s." -#: src/mactelnetd.c:1090 +#: src/mactelnetd.c:1106 #, c-format msgid "Usage: %s [-fnoh]\n" msgstr "Използване: %s [-fnoh]\n" -#: src/mactelnetd.c:1095 +#: src/mactelnetd.c:1111 #, c-format msgid "" "\n" @@ -352,7 +352,7 @@ msgstr "" " -h Тази инструкция.\n" "\n" -#: src/mactelnetd.c:1104 +#: src/mactelnetd.c:1120 #, c-format msgid "" "\n" @@ -369,45 +369,45 @@ msgstr "" " -h Тази инструкция.\n" "\n" -#: src/mactelnetd.c:1115 src/macping.c:189 +#: src/mactelnetd.c:1131 src/macping.c:189 #, c-format msgid "You need to have root privileges to use %s.\n" msgstr "Необходими са права на потребител root, за да използвате %s.\n" -#: src/mactelnetd.c:1185 +#: src/mactelnetd.c:1201 #, c-format msgid "MNDP: Error binding to %s:%d, %s\n" msgstr "MNDP: Грешка при свързване от %s:%d, %s\n" -#: src/mactelnetd.c:1190 +#: src/mactelnetd.c:1206 #, c-format msgid "Bound to %s:%d" msgstr "Свързване от %s:%d" -#: src/mactelnetd.c:1224 +#: src/mactelnetd.c:1240 msgid "Unable to find any valid network interfaces\n" msgstr "Няма валидни мрежови интерфейси\n" -#: src/mactelnetd.c:1269 src/interfaces.c:284 +#: src/mactelnetd.c:1285 src/interfaces.c:284 msgid "Network change detected" msgstr "Промяна в мрежата открита" -#: src/mactelnetd.c:1316 +#: src/mactelnetd.c:1332 #, c-format msgid "(%d) Connection to user %s closed." msgstr "(%d) Връзката към потребител %s е прекъсната." -#: src/mactelnetd.c:1324 +#: src/mactelnetd.c:1340 #, c-format msgid "(%d) Waiting for ack\n" msgstr "(%d) Изчакване на потвърждение\n" -#: src/mactelnetd.c:1340 +#: src/mactelnetd.c:1356 #, c-format msgid "(%d) Session timed out" msgstr "(%d) Изтекло време за изчакване на сесията" -#: src/mactelnetd.c:1343 +#: src/mactelnetd.c:1359 msgid "Timeout\r\n" msgstr "Изтекло време за изчакване\r\n" @@ -619,12 +619,12 @@ msgstr "намерен\n" msgid "FATAL ERROR: Function returned NULL at %s:%d: %s;\n" msgstr "ФАТАЛНА ГРЕШКА: Функцията върна NULL на %s:%d: %s;\n" -#: src/mtwei.c:154 +#: src/mtwei.c:162 #, c-format msgid "Cannot mix gamma into pubkey: %s\n" msgstr "Не може да се смеси гама в обща ключ: %s\n" -#: src/mtwei.c:192 +#: src/mtwei.c:200 #, c-format msgid "Cannot make a public key: %s\n" msgstr "Не може да се направи общ ключ: %s\n" diff --git a/po/nb.po b/po/nb.po index 5c6dee7..25242b6 100644 --- a/po/nb.po +++ b/po/nb.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: mactelnet\n" "Report-Msgid-Bugs-To: haakon.nessjoen@gmail.com\n" -"POT-Creation-Date: 2024-04-22 21:15+0200\n" +"POT-Creation-Date: 2024-07-30 10:37+0200\n" "PO-Revision-Date: 2024-04-22 19:37+0200\n" "Last-Translator: Håkon Nessjøen \n" "Language-Team: Håkon Nessjøen \n" @@ -175,7 +175,7 @@ msgstr "Passord: " msgid "Connecting to %s..." msgstr "Kobler til %s…" -#: src/mactelnet.c:762 src/mactelnetd.c:258 src/mactelnetd.c:1163 +#: src/mactelnet.c:762 src/mactelnetd.c:259 src/mactelnetd.c:1179 #, c-format msgid "Error binding to %s:%d, %s\n" msgstr "Problemer med å binde til %s:%d, %s\n" @@ -195,110 +195,110 @@ msgstr "ferdig\n" msgid "Username too long\n" msgstr "Brukernavn for langt\n" -#: src/mactelnetd.c:260 +#: src/mactelnetd.c:261 #, c-format msgid "Error binding to %s:%d on %s\n" msgstr "Problemer med å binde til %s:%d på %s\n" -#: src/mactelnetd.c:264 +#: src/mactelnetd.c:265 #, c-format msgid "Using %s to transmit packets from %s\n" msgstr "Bruker %s til å sende pakker fra %s\n" -#: src/mactelnetd.c:469 +#: src/mactelnetd.c:473 #, c-format msgid "(%d) Invalid login by %s." msgstr "(%d) Ugyldig login av %s." -#: src/mactelnetd.c:472 +#: src/mactelnetd.c:476 msgid "Login failed, incorrect username or password\r\n" msgstr "Login feilet, ugyldig brukernavn eller passord\r\n" -#: src/mactelnetd.c:489 +#: src/mactelnetd.c:493 msgid "Terminal error\r\n" msgstr "Terminalfeil\r\n" -#: src/mactelnetd.c:503 src/mactelnetd.c:511 +#: src/mactelnetd.c:507 src/mactelnetd.c:515 #, c-format msgid "(%d) Error allocating memory." msgstr "(%d) Klarer ikke allokere minne." -#: src/mactelnetd.c:505 src/mactelnetd.c:513 +#: src/mactelnetd.c:509 src/mactelnetd.c:517 msgid "System error, out of memory\r\n" msgstr "Systemfeil, minne fullt\r\n" -#: src/mactelnetd.c:518 +#: src/mactelnetd.c:522 #, c-format msgid "(%d) Login ok, but local user not accessible (%s)." msgstr "(%d) Login ok, men lokal bruker er ikke tilgjengelig (%s)." -#: src/mactelnetd.c:521 +#: src/mactelnetd.c:525 msgid "Local user not accessible\r\n" msgstr "Lokal bruker er ikke tilgjengelig\r\n" -#: src/mactelnetd.c:532 +#: src/mactelnetd.c:536 #, c-format msgid "Error opening %s: %s" msgstr "Klarer ikke åpne %s: %s" -#: src/mactelnetd.c:534 +#: src/mactelnetd.c:538 msgid "Error opening terminal\r\n" msgstr "Klarer ikke åpne terminal\r\n" -#: src/mactelnetd.c:545 +#: src/mactelnetd.c:549 #, c-format msgid "(%d) User %s logged in." msgstr "(%d) Bruker %s logget inn." -#: src/mactelnetd.c:580 +#: src/mactelnetd.c:584 #, c-format msgid "(%d) Could not log in %s (%d:%d): setuid/setgid: %s" msgstr "(%d) Kunne ikke logge inn %s (%d:%d): setuid/setgid: %s" -#: src/mactelnetd.c:583 +#: src/mactelnetd.c:587 msgid "Internal error\r\n" msgstr "Intern feil\r\n" -#: src/mactelnetd.c:589 +#: src/mactelnetd.c:593 #, c-format msgid "(%d) User %s disconnected with " msgstr "(%d) Bruker %s frakoblet med " -#: src/mactelnetd.c:715 +#: src/mactelnetd.c:731 #, c-format msgid "(%d) Invalid mtwei key by %s." msgstr "(%d) Ugyldig mtwei nøkkel av %s." -#: src/mactelnetd.c:738 +#: src/mactelnetd.c:754 #, c-format msgid "(%d) Unhandeled control packet type: %d, length: %d" msgstr "(%d) Uhåndtert kontrollpakke-type: %d, lengde: %d" -#: src/mactelnetd.c:742 +#: src/mactelnetd.c:758 #, c-format msgid "(%d) Unhandeled control packet type: %d, in state: %d, length: %d" msgstr "(%d) Uhåndtert kontrollpakke-type: %d, i tilstand: %d, lengde: %d" -#: src/mactelnetd.c:798 +#: src/mactelnetd.c:814 #, c-format msgid "(%d) New connection from %s." msgstr "(%d) Ny tilkobling fra %s." -#: src/mactelnetd.c:827 src/mactelnetd.c:1318 +#: src/mactelnetd.c:843 src/mactelnetd.c:1334 #, c-format msgid "(%d) Connection closed." msgstr "(%d) Tilkobling lukket." -#: src/mactelnetd.c:882 +#: src/mactelnetd.c:898 #, c-format msgid "(%d) Unhandeled packet type: %d" msgstr "(%d) Uhåndtert pakke-type: %d" -#: src/mactelnetd.c:957 +#: src/mactelnetd.c:973 msgid "Was not able to send any MNDP packets" msgstr "Klarte ikke sende MNDP pakker" -#: src/mactelnetd.c:966 +#: src/mactelnetd.c:982 msgid "" "\r\n" "\r\n" @@ -308,29 +308,29 @@ msgstr "" "\r\n" "Tjener avslutter.\r\n" -#: src/mactelnetd.c:968 +#: src/mactelnetd.c:984 msgid "Daemon shutting down" msgstr "Tjener avslutter" -#: src/mactelnetd.c:1001 +#: src/mactelnetd.c:1017 msgid "SIGHUP: Reloading interfaces" msgstr "SIGHUP: Laster grensesnitt på nytt" -#: src/mactelnetd.c:1016 +#: src/mactelnetd.c:1032 msgid "No devices found! Exiting.\n" msgstr "Ingen enheter funnet! Avslutter.\n" -#: src/mactelnetd.c:1030 +#: src/mactelnetd.c:1046 #, c-format msgid "(%d) Connection closed because interface %s is gone." msgstr "(%d) Tilkobling lukket på grunn av at %s er borte." -#: src/mactelnetd.c:1090 +#: src/mactelnetd.c:1106 #, c-format msgid "Usage: %s [-fnoh]\n" msgstr "Bruksmåte: %s [-fnoh]\n" -#: src/mactelnetd.c:1095 +#: src/mactelnetd.c:1111 #, c-format msgid "" "\n" @@ -349,7 +349,7 @@ msgstr "" " -h Denne hjelpen.\n" "\n" -#: src/mactelnetd.c:1104 +#: src/mactelnetd.c:1120 #, c-format msgid "" "\n" @@ -366,45 +366,45 @@ msgstr "" " -h Denne hjelpen.\n" "\n" -#: src/mactelnetd.c:1115 src/macping.c:189 +#: src/mactelnetd.c:1131 src/macping.c:189 #, c-format msgid "You need to have root privileges to use %s.\n" msgstr "Du trenger superbruker-rettigheter for å bruke %s.\n" -#: src/mactelnetd.c:1185 +#: src/mactelnetd.c:1201 #, c-format msgid "MNDP: Error binding to %s:%d, %s\n" msgstr "MNDP: Klarte ikke binde til %s:%d, %s\n" -#: src/mactelnetd.c:1190 +#: src/mactelnetd.c:1206 #, c-format msgid "Bound to %s:%d" msgstr "Bundet til %s:%d" -#: src/mactelnetd.c:1224 +#: src/mactelnetd.c:1240 msgid "Unable to find any valid network interfaces\n" msgstr "Klarte ikke finne noen gyldige nettverksgrensesnitt\n" -#: src/mactelnetd.c:1269 src/interfaces.c:284 +#: src/mactelnetd.c:1285 src/interfaces.c:284 msgid "Network change detected" msgstr "Nettverksforandring oppdaget" -#: src/mactelnetd.c:1316 +#: src/mactelnetd.c:1332 #, c-format msgid "(%d) Connection to user %s closed." msgstr "(%d) Tilkobling til bruker %s lukket." -#: src/mactelnetd.c:1324 +#: src/mactelnetd.c:1340 #, c-format msgid "(%d) Waiting for ack\n" msgstr "(%d) Venter på ack\n" -#: src/mactelnetd.c:1340 +#: src/mactelnetd.c:1356 #, c-format msgid "(%d) Session timed out" msgstr "(%d) Sesjonen utgikk på tidsavbrudd" -#: src/mactelnetd.c:1343 +#: src/mactelnetd.c:1359 msgid "Timeout\r\n" msgstr "Tidsavbrudd\r\n" @@ -607,12 +607,12 @@ msgstr "funnet\n" msgid "FATAL ERROR: Function returned NULL at %s:%d: %s;\n" msgstr "FATAL FEIL: Funksjonen returnerte NULL ved %s:%d: %s;\n" -#: src/mtwei.c:154 +#: src/mtwei.c:162 #, c-format msgid "Cannot mix gamma into pubkey: %s\n" msgstr "Kan ikke blande gamma inn i offentlig nøkkel: %s\n" -#: src/mtwei.c:192 +#: src/mtwei.c:200 #, c-format msgid "Cannot make a public key: %s\n" msgstr "Kan ikke lage en offentlig nøkkel: %s\n" diff --git a/src/Makefile.am b/src/Makefile.am index 25007a0..c51cb10 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,6 +16,10 @@ mactelnetd_SOURCES = config.h mactelnetd.c protocol.c protocol.h interfaces.c in mactelnetd_CFLAGS = -DFROM_MACTELNETD -DUSERSFILE='"$(sysconfdir)/mactelnetd.users"' mactelnetd_LDADD = $(CRYPTO_LIBS) $(COREFOUNDATION_LIBS) $(SYSTEMCONFIGURATION_LIBS) +savepass_SOURCES = config.h savepass.c mtwei.c mtwei.h users.c users.h extra.h utlist.h +savepass_CFLAGS = -DUSERSFILE='"$(sysconfdir)/mactelnetd.users"' +savepass_LDADD = $(CRYPTO_LIBS) + mndp_SOURCES = config.h mndp.c mndp.h protocol.c protocol.h extra.h macping_SOURCES = config.h macping.c interfaces.c interfaces.h protocol.c protocol.h extra.h utlist.h diff --git a/src/mactelnet.c b/src/mactelnet.c index a51d9fd..01a4f6e 100644 --- a/src/mactelnet.c +++ b/src/mactelnet.c @@ -16,6 +16,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "config.h" #define _DEFAULT_SOURCE #include #include @@ -53,7 +54,14 @@ #endif #include -#include "config.h" +#if (HAVE_READPASSPHRASE == 1) +#include +#elif (HAVE_BSDREADPASSPHRASE == 1) +#include +#else +#warning "Falling back to getpass(3), which is marked obsolete!" +#endif + #include "mtwei.h" #include "protocol.h" #include "console.h" @@ -717,8 +725,12 @@ int main(int argc, char **argv) { } if (!have_password) { - char *tmp; - tmp = getpass(quiet_mode ? "" : _("Password: ")); +#if (HAVE_READPASSPHRASE == 1 || HAVE_BSDREADPASSPHRASE == 1) + static char pwd[200]; + char *tmp = readpassphrase(_("Password: "), (char *)&pwd, 200, RPP_ECHO_OFF); +#else + char *tmp = getpass(_("Password: ")); +#endif #if defined(_POSIX_MEMLOCK_RANGE) && _POSIX_MEMLOCK_RANGE > 0 mlock(password, sizeof(password)); #endif diff --git a/src/mactelnetd.c b/src/mactelnetd.c index a560544..1ccd839 100644 --- a/src/mactelnetd.c +++ b/src/mactelnetd.c @@ -33,6 +33,7 @@ #include #include #include +#include #if defined(__APPLE__) #include #include @@ -81,6 +82,15 @@ #include #include +#if (HAVE_READPASSPHRASE == 1) +#include +#elif (HAVE_BSDREADPASSPHRASE == 1) +#include +#else +#warning "Falling back to getpass(3), which is marked obsolete!" +#include +#endif + #include "protocol.h" #include "console.h" #include "interfaces.h" @@ -442,7 +452,7 @@ static void user_login(struct mt_connection *curconn, struct mt_mactelnet_hdr *p act_pass_len = strlen(user->password); act_pass_len = act_pass_len <= 82 ? act_pass_len : 82; - if (use_md5) { + if (use_md5 && user->hashed == 0) { /* Concat string of 0 + password + pass_salt */ hashdata[0] = 0; memcpy(hashdata + 1, user->password, act_pass_len); @@ -458,10 +468,21 @@ static void user_login(struct mt_connection *curconn, struct mt_mactelnet_hdr *p EVP_DigestFinal_ex(context, hashsum + 1, &md_len); EVP_MD_CTX_free(context); hashsum[0] = 0; + } else if (use_md5 && user->hashed == 1) { + // Provoke invalid login response + user = NULL; + syslog(LOG_NOTICE, _("(%d) User %s tried to login with md5 authentication, but user is not saved in plaintext"), curconn->seskey, curconn->username); } else { - mtwei_id(curconn->username, user->password, curconn->pass_salt + MTWEI_PUBKEY_LEN, (uint8_t *)hashdata); - mtwei_docryptos(&mtwei, curconn->private_key, curconn->client_key, curconn->pass_salt, (uint8_t *)hashdata, - hashsum); + if (user->hashed == 1) { + // copy validator from userfile + memcpy(hashdata, user->password, 32); + mtwei_docryptos(&mtwei, curconn->private_key, curconn->client_key, curconn->pass_salt, + (uint8_t *)hashdata, hashsum); + } else { + mtwei_id(curconn->username, user->password, curconn->pass_salt + MTWEI_PUBKEY_LEN, (uint8_t *)hashdata); + mtwei_docryptos(&mtwei, curconn->private_key, curconn->client_key, curconn->pass_salt, + (uint8_t *)hashdata, hashsum); + } } } @@ -499,18 +520,12 @@ static void user_login(struct mt_connection *curconn, struct mt_mactelnet_hdr *p if (slavename != NULL) { pid_t pid; struct stat sb; - struct passwd *user = (struct passwd *)malloc(sizeof(struct passwd)); - struct passwd *tmpuser = user; - char *buffer; + struct passwd srcuser; + struct passwd *user; + const size_t bufsize = 16384; + char * buffer; - if (user == NULL) { - syslog(LOG_CRIT, _("(%d) Error allocating memory."), curconn->seskey); - /*_ Please include both \r and \n in translation, this is needed for the terminal emulator. */ - abort_connection(curconn, pkthdr, _("System error, out of memory\r\n")); - return; - } - - buffer = (char *)malloc(1024); + buffer = (char *)malloc(bufsize); if (buffer == NULL) { syslog(LOG_CRIT, _("(%d) Error allocating memory."), curconn->seskey); /*_ Please include both \r and \n in translation, this is needed for the terminal emulator. */ @@ -518,12 +533,12 @@ static void user_login(struct mt_connection *curconn, struct mt_mactelnet_hdr *p return; } - if (getpwnam_r(curconn->username, user, buffer, 1024, &tmpuser) != 0) { + // TODO: support ERANGE + if (getpwnam_r(curconn->username, &srcuser, buffer, bufsize, &user) != 0 || user == NULL) { syslog(LOG_WARNING, _("(%d) Login ok, but local user not accessible (%s)."), curconn->seskey, curconn->username); /*_ Please include both \r and \n in translation, this is needed for the terminal emulator. */ - abort_connection(curconn, pkthdr, _("Local user not accessible\r\n")); - free(user); + abort_connection(curconn, pkthdr, _("Error: Local user not accessible\r\n")); free(buffer); return; } @@ -535,6 +550,7 @@ static void user_login(struct mt_connection *curconn, struct mt_mactelnet_hdr *p if (curconn->slavefd == -1) { syslog(LOG_ERR, _("Error opening %s: %s"), slavename, strerror(errno)); /*_ Please include both \r and \n in translation, this is needed for the terminal emulator. */ + free(buffer); abort_connection(curconn, pkthdr, _("Error opening terminal\r\n")); list_remove_connection(curconn); return; @@ -610,7 +626,6 @@ static void user_login(struct mt_connection *curconn, struct mt_mactelnet_hdr *p execl(user->pw_shell, user->pw_shell, "-", (char *)0); exit(0); // just to be sure. } - free(user); free(buffer); close(curconn->slavefd); curconn->pid = pid; @@ -701,20 +716,27 @@ static void handle_data_packet(struct mt_connection *curconn, struct mt_mactelne if ((user = find_user(curconn->username)) != NULL) { curconn->have_pass_salt = 1; uint8_t validator[32]; - mtwei_id(curconn->username, user->password, curconn->pass_salt + MTWEI_PUBKEY_LEN, validator); + + if (user->hashed) { + memcpy(validator, user->password, 32); + memcpy(curconn->pass_salt + MTWEI_PUBKEY_LEN, user->salt, 16); + } else { + mtwei_id(curconn->username, user->password, curconn->pass_salt + MTWEI_PUBKEY_LEN, + validator); + } curconn->private_key = mtwei_keygen(&mtwei, curconn->pass_salt, validator); } else { /* Continue auth flow, so we do not let an attacker figure out if the user exists or not. - we need to set a fake private key, so we can continue the auth flow until the user sends password, - then we can send "invalid login" message, and disconnect the user. */ + we need to set a fake private key, so we can continue the auth flow until the user sends + password, then we can send "invalid login" message, and disconnect the user. */ curconn->have_pass_salt = 1; curconn->invalid_login = 1; uint8_t validator[32]; char username[33]; - RAND_bytes(username, 32); + RAND_bytes((unsigned char*)username, 32); username[32] = 0; char password[33]; - RAND_bytes(password, 32); + RAND_bytes((unsigned char*)password, 32); password[32] = 0; mtwei_id(username, password, curconn->pass_salt + MTWEI_PUBKEY_LEN, validator); @@ -1051,6 +1073,96 @@ void sighup_handler() { } } +static int main_add_user(char *username, char *password) { + char user[32]; + char pwd[200]; + + if (username == NULL) { + printf(_("Username: ")); + fflush(stdout); + (void)scanf("%31s", user); + + if (strlen(user) == 0) { + fprintf(stderr, _("Username must be specified.\n")); + return 1; + } + username = user; + } + + if (strlen(username) > 32) { + fprintf(stderr, _("Username too long.\n")); + return 1; + } + + struct passwd *user_entry = getpwnam(username); + if (user_entry == NULL) { + fprintf(stderr, _("Warning: Local user '%s' does not exist.\n"), username); + } + + if (password == NULL) { +#if (HAVE_READPASSPHRASE == 1 || HAVE_BSDREADPASSPHRASE == 1) + char *tmp = readpassphrase(_("Password: "), (char *)&pwd, 200, RPP_ECHO_OFF); +#else + char *tmp = getpass(_("Password: ")); +#endif + if (tmp == NULL || strlen(tmp) == 0) { + fprintf(stderr, _("Password must be specified.\n")); + return 1; + } + password = tmp; + } + + int result = add_user(username, password); + if (result == 1) { + printf(_("User %s was added.\n"), username); + } else if (result == 2) { + printf(_("User %s was updated.\n"), username); + } else { + fprintf(stderr, _("Failed to add user %s.\n"), username); + return 1; + } + return 0; +} + +static int main_delete_user(char *username) { + if (username == NULL) { + fprintf(stderr, _("Username must be specified.\n")); + return 1; + } + + int result = add_user(username, NULL); + if (result == 2) { + printf(_("User %s was deleted.\n"), username); + } else if (result == 1) { + printf(_("User %s did not exist.\n"), username); + } else { + fprintf(stderr, _("Failed to delete user %s.\n"), username); + return 1; + } + return 0; +} + +static int main_list_users() { + struct mt_credentials *user; + + printf(_("Users:\n")); + DL_FOREACH(mt_users, user) { + struct passwd *user_entry = getpwnam(user->username); + + if (user_entry == NULL) { + printf("\t%s (%s)\n", user->username, _("local user not found!")); + } else if (user_entry->pw_uid == 0 || user_entry->pw_gid == 0) { + printf("\t%s (%s)\n", user->username, _("has root access!")); + } else if (!user->hashed) { + printf("\t%s (%s)\n", user->username, _("plain-text password!")); + } else { + printf("\t%s\n", user->username); + } + } + printf("\n"); + return 0; +} + /* * TODO: Rewrite main() when all sub-functionality is tested */ @@ -1066,6 +1178,11 @@ int main(int argc, char **argv) { int print_help = 0; int foreground = 0; int interface_count = 0; + char add_user = 0; + char list_users = 0; + char *add_user_name = NULL; + char *add_user_password = NULL; + char *delete_user = NULL; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); @@ -1074,7 +1191,7 @@ int main(int argc, char **argv) { #if !defined(__APPLE__) while ((c = getopt(argc, argv, "fnovh?")) != -1) { #else - while ((c = getopt(argc, argv, "novh")) != -1) { + while ((c = getopt(argc, argv, "novhlau:p:d:")) != -1) { #endif switch (c) { case 'f': @@ -1094,6 +1211,26 @@ int main(int argc, char **argv) { exit(0); break; + case 'a': + add_user = 1; + break; + + case 'u': + add_user_name = optarg; + break; + + case 'p': + add_user_password = optarg; + break; + + case 'd': + delete_user = optarg; + break; + + case 'l': + list_users = 1; + break; + case 'h': case '?': print_help = 1; @@ -1103,24 +1240,36 @@ int main(int argc, char **argv) { if (print_help) { print_version(); - fprintf(stderr, _("Usage: %s [-fnoh]\n"), argv[0]); + fprintf(stderr, _("Usage: %s [-fnoh]|-a [-u |-p ]|[-d ]\n"), argv[0]); if (print_help) { #if !defined(__APPLE__) /*_ This is the usage output for operating systems other than MacOS */ fprintf(stderr, _("\nParameters:\n" - " -f Run process in foreground.\n" - " -n Do not use broadcast packets. Just a tad less insecure.\n" - " -o Use MD5 for password hashing.\n" - " -h This help.\n" + " -f Run process in foreground.\n" + " -n Do not use broadcast packets. Just a tad less insecure.\n" + " -o Use MD5 for password hashing.\n" + " -l List users from userfile.\n" + " -a Add a new user.\n" + " -u [user] Optionally set username to add with -a.\n" + " -p [password] Optionally set password for -a command.\n" + " -d [user] Delete user.\n" + " -h This help.\n" + "\n\nIf any of -a, -d, -l or -h is specified, the server will not start.\n" "\n")); #else /*_ This is the usage output for MacOS which always runs in the forground as it should be daemonized by launchd */ fprintf(stderr, _("\nParameters:\n" - " -n Do not use broadcast packets. Just a tad less insecure.\n" - " -o Use MD5 for password hashing.\n" - " -h This help.\n" + " -n Do not use broadcast packets. Just a tad less insecure.\n" + " -o Use MD5 for password hashing.\n" + " -l List users from userfile.\n" + " -a Add a new user.\n" + " -u [user] Optionally set username to add with -a.\n" + " -p [password] Optionally set password for -a command.\n" + " -d [user] Delete user.\n" + " -h This help.\n" + "\n\nIf any of -a, -d, -l or -h is specified, the server will not start.\n" "\n")); #endif } @@ -1135,6 +1284,14 @@ int main(int argc, char **argv) { /* Try to read user file */ read_userfile(); + if (add_user) { + return main_add_user(add_user_name, add_user_password); + } else if (delete_user) { + return main_delete_user(delete_user); + } else if (list_users) { + return main_list_users(); + } + /* Seed randomizer */ srand(time(NULL)); diff --git a/src/users.c b/src/users.c index e8b3aeb..05ec51b 100644 --- a/src/users.c +++ b/src/users.c @@ -22,7 +22,14 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include "mtwei.h" #include "extra.h" #include "users.h" #include "utlist.h" @@ -31,8 +38,95 @@ struct mt_credentials *mt_users = NULL; +static int parseLine(char *line, char **username, char **password, char **salt) { + char *user; + char *pass; + char *sal; + + user = strtok(line, ":"); + int userlen = strlen(user); + + if (strstr(user+userlen+1, ":") != NULL) { + pass = strtok(NULL, ":"); + sal = strtok(NULL, "\n"); + } else { + pass = strtok(NULL, "\n"); + sal = NULL; + } + + if (user == NULL || pass == NULL || user[0] == '#') { + return 0; + } + + if (userlen > MT_CRED_USERLEN) { + userlen = MT_CRED_USERLEN; + } + user[userlen] = '\0'; + if (strlen(pass) > MT_CRED_LEN) { + pass[MT_CRED_LEN] = '\0'; + } + if (sal != NULL && strlen(sal) > MT_CRED_SALTLEN * 2) { + sal[MT_CRED_SALTLEN * 2] = '\0'; + } + if (sal != NULL && strlen(pass) > MT_CRED_HASHLEN * 2) { + pass[MT_CRED_HASHLEN * 2] = '\0'; + } + + *username = user; + *password = pass; + *salt = sal; + + return 1; +} + +// Returns 1 if the file is ok, 0 if not +static int check_user_file(struct stat *info) { + if (stat(USERSFILE, info) != 0) { + fprintf(stderr, _("Error stating file %s: %s\n"), USERSFILE, strerror(errno)); + return 0; + } + + struct passwd *pwd = getpwuid(info->st_uid); + if (pwd == NULL) { + fprintf(stderr, _("Error getting user information for uid %d: %s\n"), info->st_uid, strerror(errno)); + return 0; + } + + if (strcmp(pwd->pw_name, "root") != 0) { + fprintf(stderr, _("Error: %s is not owned by root\n"), USERSFILE); + return 0; + } + + if (info->st_mode & S_IWOTH || info->st_mode & S_IWGRP) { + fprintf(stderr, + _("Error: %s is writable by others, It should have permissions set to 0600 for better security\n"), + USERSFILE); + return 0; + } + + return 1; +} + void read_userfile() { + int lineno = 0; struct mt_credentials *cred, *tmp; + + struct stat info; + int file_ok = check_user_file(&info); + if (!file_ok) { + int counter = 0; + DL_COUNT(mt_users, cred, counter); + if (counter == 0) { + // If the file is not usable, and we have no users, we should abort + fprintf(stderr, _("Error: %s is invalid and no users known, aborting.\n"), USERSFILE); + exit(EXIT_FAILURE); + } + // If the file is not owned by root, or if it is writable by others, but we have read the users file before, we can continue + // without the updated users file. + fprintf(stderr, _("Warning: User file '%s' is not readable, falling back to known users.\n"), USERSFILE); + return; + } + FILE *file = fopen(USERSFILE, "r"); char line[BUFSIZ]; @@ -49,12 +143,12 @@ void read_userfile() { while (fgets(line, sizeof line, file)) { char *user; char *password; + char *salt; size_t size; - user = strtok(line, ":"); - password = strtok(NULL, "\n"); + lineno++; - if (user == NULL || password == NULL || user[0] == '#') { + if (!parseLine(line, &user, &password, &salt)) { continue; } @@ -67,8 +161,40 @@ void read_userfile() { /* verify that the username & password will be '\0' terminated */ memcpy(cred->username, user, size = (strlen(user) < MT_CRED_LEN ? strlen(user) : MT_CRED_LEN - 1)); cred->username[size] = '\0'; - memcpy(cred->password, password, size = (strlen(password) < MT_CRED_LEN ? strlen(password) : MT_CRED_LEN - 1)); - cred->password[size] = '\0'; + if (salt != NULL) { + if (strlen(password) != MT_CRED_HASHLEN * 2) { + fprintf(stderr, _("Warning: Invalid password hash on line %d of user file\n"), lineno); + free(cred); + continue; + } + if (strlen(salt) != MT_CRED_SALTLEN * 2) { + fprintf(stderr, _("Warning: Invalid salt on line %d of user file\n"), lineno); + free(cred); + continue; + } + long readlen; + unsigned char *binsalt; + if ((binsalt = OPENSSL_hexstr2buf(salt, &readlen)) == NULL || readlen != MT_CRED_SALTLEN) { + fprintf(stderr, _("Warning: Invalid salt on line %d of user file\n"), lineno); + free(cred); + continue; + } + memcpy(cred->salt, binsalt, MT_CRED_SALTLEN); + + readlen = 0; + unsigned char *binpass; + if ((binpass = OPENSSL_hexstr2buf(password, &readlen)) == NULL || readlen != MT_CRED_HASHLEN) { + fprintf(stderr, _("Warning: Invalid password hash on line %d of user file\n"), lineno); + free(cred); + continue; + } + memcpy(cred->password, binpass, MT_CRED_HASHLEN); + cred->hashed = 1; + } else { + memcpy(cred->password, password, + size = (strlen(password) < MT_CRED_LEN ? strlen(password) : MT_CRED_LEN - 1)); + cred->password[size] = '\0'; + } DL_APPEND(mt_users, cred); } fclose(file); @@ -84,3 +210,121 @@ struct mt_credentials *find_user(char *username) { } return NULL; } + +int add_user(const char *username, const char *password) { + FILE *rfile; + FILE *wfile; + char line[BUFSIZ]; + char linecopy[BUFSIZ]; + unsigned char newsalt[MT_CRED_SALTLEN]; + unsigned char newhash[MT_CRED_HASHLEN]; + unsigned int md_len; + char found = 0; + int lineno = 0; + + // Check that the file USERSFILE is owned by root with stat(), and that it is not writable by others + // If not, exit with failure + struct stat info; + int is_ok = check_user_file(&info); + if (!is_ok) { + exit(EXIT_FAILURE); + } + + // Open the password file + rfile = fopen(USERSFILE, "r"); + if (!rfile) { + fprintf(stderr, _("Error opening password file %s: %s\n"), USERSFILE, strerror(errno)); + exit(EXIT_FAILURE); + } + wfile = fopen(USERSFILE ".tmp", "wb"); + if (!wfile) { + fprintf(stderr, _("Error opening temporary password file for writing %s: %s\n"), USERSFILE ".tmp", + strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fchown(fileno(wfile), info.st_uid, info.st_gid) != 0) { + fprintf(stderr, _("Error changing ownership of temporary password file %s: %s\n"), USERSFILE ".tmp", + strerror(errno)); + fclose(wfile); + unlink(USERSFILE ".tmp"); + exit(EXIT_FAILURE); + } + + if (fchmod(fileno(wfile), info.st_mode) != 0) { + fprintf(stderr, _("Error changing permissions of temporary password file %s: %s\n"), USERSFILE ".tmp", + strerror(errno)); + fclose(wfile); + unlink(USERSFILE ".tmp"); + exit(EXIT_FAILURE); + } + + // Generate a random salt + if (!RAND_bytes(newsalt, sizeof(newsalt))) { + fprintf(stderr, _("Error generating random salt.\n")); + exit(EXIT_FAILURE); + } + + if (password != NULL) { + mtwei_id(username, password, newsalt, newhash); + } + + while (fgets(line, sizeof line, rfile)) { + char *user; + char *pass; + char *sal; + + lineno++; + + memcpy(linecopy, line, sizeof linecopy); + if (!parseLine(linecopy, &user, &pass, &sal)) { + fputs(line, wfile); + continue; + } + + if (!found && strcmp(user, username) == 0) { + if (password == NULL) { + // Delete the user + found = 1; + continue; + } + fprintf(wfile, "%s:", username); + char output[MT_CRED_HASHLEN * 2 + 1]; + OPENSSL_buf2hexstr_ex(output, sizeof(output), NULL, newhash, MT_CRED_HASHLEN, '\0'); + fputs(output, wfile); + fputs(":", wfile); + OPENSSL_buf2hexstr_ex(output, sizeof(output), NULL, newsalt, MT_CRED_SALTLEN, '\0'); + fputs(output, wfile); + fputs("\n", wfile); + found = 1; + } else { + fputs(line, wfile); + } + } + + // Non-existing user, append to the end of the file + if (!found && password != NULL) { + // Write username, salt, and hashed password to the file + fprintf(wfile, "%s:", username); + char output[MT_CRED_HASHLEN * 2 + 1]; + OPENSSL_buf2hexstr_ex(output, sizeof(output), NULL, newhash, MT_CRED_HASHLEN, '\0'); + fputs(output, wfile); + fputs(":", wfile); + OPENSSL_buf2hexstr_ex(output, sizeof(output), NULL, newsalt, MT_CRED_SALTLEN, '\0'); + fputs(output, wfile); + fputs("\n", wfile); + } + + // Close the password file + fclose(wfile); + fclose(rfile); + + // Rename the temporary file to the password file + if (rename(USERSFILE ".tmp", USERSFILE) != 0) { + fprintf(stderr, "Error renaming temporary password file to %s: %s\n", USERSFILE, strerror(errno)); + unlink(USERSFILE ".tmp"); + exit(EXIT_FAILURE); + } + + return found ? 2 : 1; +} \ No newline at end of file diff --git a/src/users.h b/src/users.h index 13ae83f..c055cf4 100644 --- a/src/users.h +++ b/src/users.h @@ -20,11 +20,19 @@ #define _USERS_H 1 #define MT_CRED_LEN 100 -#define MT_CRED_MAXNUM 128 +#define MT_CRED_USERLEN 32 +#define MT_CRED_SALTLEN 16 +#define MT_CRED_HASHLEN 32 + +#if MT_CRED_LEN < MT_CRED_HASHLEN * 2 + 1 +#error "MT_CRED_LEN must be at least twice the length of MT_CRED_HASHLEN" +#endif struct mt_credentials { char username[MT_CRED_LEN]; char password[MT_CRED_LEN]; + char salt[MT_CRED_SALTLEN]; + char hashed; struct mt_credentials *prev; struct mt_credentials *next; @@ -33,6 +41,7 @@ struct mt_credentials { extern struct mt_credentials *mt_users; extern void read_userfile(); -struct mt_credentials *find_user(char *username); +extern int add_user(const char *username, const char *password); +extern struct mt_credentials *find_user(char *username); #endif