Users can now add an SSH public key on the account edit page. This will later be used to authenticate users via SSH. Signed-off-by: Lukas Fleischer <archlinux@cryptocrack.de> --- UPGRADING | 10 ++++- schema/aur-schema.sql | 1 + web/html/account.php | 7 ++-- web/lib/acctfuncs.inc.php | 78 ++++++++++++++++++++++++++++++++++---- web/template/account_edit_form.php | 5 +++ 5 files changed, 90 insertions(+), 11 deletions(-) diff --git a/UPGRADING b/UPGRADING index 863fde3..2e1b806 100644 --- a/UPGRADING +++ b/UPGRADING @@ -1,6 +1,15 @@ Upgrading ========= +From 3.1.0 to 4.0.0 +------------------- + +1. Add a field for the SSH public key to the Users table: + +---- +ALTER TABLE Users ADD COLUMN SSHPubKey VARCHAR(4096) NULL DEFAULT NULL; +---- + From 3.0.0 to 3.1.0 ------------------- @@ -16,7 +25,6 @@ ALTER TABLE Licenses MODIFY Name VARCHAR(255) NOT NULL; ALTER TABLE Groups MODIFY Name VARCHAR(255) NOT NULL; ALTER TABLE PackageDepends MODIFY DepCondition VARCHAR(255); ALTER TABLE PackageRelations MODIFY RelCondition VARCHAR(255); ----- From 2.3.1 to 3.0.0 ------------------- diff --git a/schema/aur-schema.sql b/schema/aur-schema.sql index 0efae93..750ee21 100644 --- a/schema/aur-schema.sql +++ b/schema/aur-schema.sql @@ -32,6 +32,7 @@ CREATE TABLE Users ( LangPreference VARCHAR(5) NOT NULL DEFAULT 'en', IRCNick VARCHAR(32) NOT NULL DEFAULT '', PGPKey VARCHAR(40) NULL DEFAULT NULL, + SSHPubKey VARCHAR(4096) NULL DEFAULT NULL, LastLogin BIGINT UNSIGNED NOT NULL DEFAULT 0, LastLoginIPAddress INTEGER UNSIGNED NOT NULL DEFAULT 0, InactivityTS BIGINT UNSIGNED NOT NULL DEFAULT 0, diff --git a/web/html/account.php b/web/html/account.php index 47cf6d2..aef240a 100644 --- a/web/html/account.php +++ b/web/html/account.php @@ -52,7 +52,7 @@ if (isset($_COOKIE["AURSID"])) { display_account_form($atype, "UpdateAccount", $row["Username"], $row["AccountTypeID"], $row["Suspended"], $row["Email"], "", "", $row["RealName"], $row["LangPreference"], - $row["IRCNick"], $row["PGPKey"], + $row["IRCNick"], $row["PGPKey"], $row["SSHPubKey"], $row["InactivityTS"] ? 1 : 0, $row["ID"]); } else { print __("You do not have permission to edit this account."); @@ -82,7 +82,8 @@ if (isset($_COOKIE["AURSID"])) { in_request("U"), in_request("T"), in_request("S"), in_request("E"), in_request("P"), in_request("C"), in_request("R"), in_request("L"), in_request("I"), - in_request("K"), in_request("J"), in_request("ID")); + in_request("K"), in_request("PK"), in_request("J"), + in_request("ID")); } } else { if ($atype == "Trusted User" || $atype == "Developer") { @@ -113,7 +114,7 @@ if (isset($_COOKIE["AURSID"])) { # display the account request form # print __("Use this form to create an account."); - display_account_form("", "NewAccount", "", "", "", "", "", "", "", $LANG); + display_account_form("", "NewAccount", "", "", "", "", "", "", "", "", $LANG); } } diff --git a/web/lib/acctfuncs.inc.php b/web/lib/acctfuncs.inc.php index 06d4311..d7578a8 100644 --- a/web/lib/acctfuncs.inc.php +++ b/web/lib/acctfuncs.inc.php @@ -54,13 +54,14 @@ function html_format_pgp_fingerprint($fingerprint) { * @param string $L The language preference of the displayed user * @param string $I The IRC nickname of the displayed user * @param string $K The PGP key fingerprint of the displayed user + * @param string $PK The SSH public key of the displayed user * @param string $J The inactivity status of the displayed user * @param string $UID The user ID of the displayed user * * @return void */ -function display_account_form($UTYPE,$A,$U="",$T="",$S="", - $E="",$P="",$C="",$R="",$L="",$I="",$K="",$J="", $UID=0) { +function display_account_form($UTYPE,$A,$U="",$T="",$S="",$E="",$P="",$C="", + $R="",$L="",$I="",$K="",$PK="",$J="", $UID=0) { global $SUPPORTED_LANGS; include("account_edit_form.php"); @@ -84,13 +85,14 @@ function display_account_form($UTYPE,$A,$U="",$T="",$S="", * @param string $L The language preference of the user * @param string $I The IRC nickname of the user * @param string $K The PGP fingerprint of the user + * @param string $PK The SSH public key of the user * @param string $J The inactivity status of the user * @param string $UID The user ID of the modified account * * @return string|void Return void if successful, otherwise return error */ -function process_account_form($UTYPE,$TYPE,$A,$U="",$T="",$S="",$E="", - $P="",$C="",$R="",$L="",$I="",$K="",$J="",$UID=0) { +function process_account_form($UTYPE,$TYPE,$A,$U="",$T="",$S="",$E="",$P="", + $C="",$R="",$L="",$I="",$K="",$PK="",$J="",$UID=0) { global $SUPPORTED_LANGS, $AUR_LOCATION; $error = ''; @@ -143,6 +145,15 @@ function process_account_form($UTYPE,$TYPE,$A,$U="",$T="",$S="",$E="", $error = __("The PGP key fingerprint is invalid."); } + if (!$error && !empty($PK)) { + if (valid_ssh_pubkey($PK)) { + $tokens = explode(" ", $PK); + $PK = $tokens[0] . " " . $tokens[1]; + } else { + $error = __("The SSH public key is invalid."); + } + } + if (($UTYPE == "User" && $T > 1) || ($UTYPE == "Trusted User" && $T > 2)) { $error = __("Cannot increase account permissions."); } @@ -185,11 +196,29 @@ function process_account_form($UTYPE,$TYPE,$A,$U="",$T="",$S="",$E="", "<strong>", htmlspecialchars($E,ENT_QUOTES), "</strong>"); } } + if (!$error) { + /* + * Check whether the SSH public key is available. + * TODO: Fix race condition. + */ + $q = "SELECT COUNT(*) FROM Users "; + $q.= "WHERE SSHPubKey = " . $dbh->quote($PK); + if ($TYPE == "edit") { + $q.= " AND ID != " . intval($UID); + } + $result = $dbh->query($q); + $row = $result->fetch(PDO::FETCH_NUM); + + if ($row[0]) { + $error = __("The SSH public key, %s%s%s, is already in use.", + "<strong>", htmlspecialchars($PK, ENT_QUOTES), "</strong>"); + } + } if ($error) { print "<ul class='errorlist'><li>".$error."</li></ul>\n"; display_account_form($UTYPE, $A, $U, $T, $S, $E, "", "", - $R, $L, $I, $K, $J, $UID); + $R, $L, $I, $K, $PK, $J, $UID); return; } @@ -211,11 +240,13 @@ function process_account_form($UTYPE,$TYPE,$A,$U="",$T="",$S="",$E="", $L = $dbh->quote($L); $I = $dbh->quote($I); $K = $dbh->quote(str_replace(" ", "", $K)); + $PK = $dbh->quote($PK); $q = "INSERT INTO Users (AccountTypeID, Suspended, "; $q.= "InactivityTS, Username, Email, Passwd, Salt, "; - $q.= "RealName, LangPreference, IRCNick, PGPKey) "; + $q.= "RealName, LangPreference, IRCNick, PGPKey, "; + $q.= "SSHPubKey) "; $q.= "VALUES (1, 0, 0, $U, $E, $P, $salt, $R, $L, "; - $q.= "$I, $K)"; + $q.= "$I, $K, $PK)"; $result = $dbh->exec($q); if (!$result) { print __("Error trying to create account, %s%s%s.", @@ -283,6 +314,7 @@ function process_account_form($UTYPE,$TYPE,$A,$U="",$T="",$S="",$E="", $q.= ", LangPreference = " . $dbh->quote($L); $q.= ", IRCNick = " . $dbh->quote($I); $q.= ", PGPKey = " . $dbh->quote(str_replace(" ", "", $K)); + $q.= ", SSHPubKey = " . $dbh->quote($PK); $q.= ", InactivityTS = " . $inactivity_ts; $q.= " WHERE ID = ".intval($UID); $result = $dbh->exec($q); @@ -797,6 +829,38 @@ function valid_pgp_fingerprint($fingerprint) { } /** + * Determine if the SSH public key is valid + * + * @param string $pubkey SSH public key to check + * + * @return bool True if the SSH public key is valid, otherwise false + */ +function valid_ssh_pubkey($pubkey) { + $valid_prefixes = array( + "ssh-rsa", "ssh-dss", "ecdsa-sha2-nistp256", + "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521", "ssh-ed25519" + ); + + $has_valid_prefix = false; + foreach ($valid_prefixes as $prefix) { + if (strpos($pubkey, $prefix . " ") === 0) { + $has_valid_prefix = true; + break; + } + } + if (!$has_valid_prefix) { + return false; + } + + $tokens = explode(" ", $pubkey); + if (empty($tokens[1])) { + return false; + } + + return (base64_encode(base64_decode($tokens[1], true)) == $tokens[1]); +} + +/** * Determine if the user account has been suspended * * @param string $id The ID of user to check if suspended diff --git a/web/template/account_edit_form.php b/web/template/account_edit_form.php index 30b26fd..767b227 100644 --- a/web/template/account_edit_form.php +++ b/web/template/account_edit_form.php @@ -93,6 +93,11 @@ </p> <p> + <label for="id_ssh"><?= __("SSH Public Key") ?>:</label> + <textarea name="PK" id="id_ssh" rows="5" cols="30"><?= htmlspecialchars($PK) ?></textarea> + </p> + + <p> <label for="id_language"><?= __("Language") ?>:</label> <select name="L" id="id_language"> <?php -- 2.0.0