mirror of
https://github.com/webmin/webmin.git
synced 2026-02-03 06:03:28 +00:00
More work on two-factor support, including new API for provider UI
This commit is contained in:
@@ -75,6 +75,7 @@ while(<PWFILE>) {
|
||||
$user{'temppass'} = int($user[10]);
|
||||
$user{'twofactor_provider'} = $user[11];
|
||||
$user{'twofactor_id'} = $user[12];
|
||||
$user{'twofactor_apikey'} = $user[13];
|
||||
$user{'modules'} = $acl{$user[0]};
|
||||
$user{'lang'} = $gconfig{"lang_$user[0]"};
|
||||
$user{'notabs'} = $gconfig{"notabs_$user[0]"};
|
||||
@@ -464,7 +465,8 @@ else {
|
||||
$user{'nochange'},":",
|
||||
$user{'temppass'},":",
|
||||
$user{'twofactor_provider'},":",
|
||||
$user{'twofactor_id'},
|
||||
$user{'twofactor_id'},":",
|
||||
$user{'twofactor_apikey'},
|
||||
"\n");
|
||||
&close_tempfile(PWFILE);
|
||||
&unlock_file($miniserv{'userfile'});
|
||||
@@ -652,7 +654,8 @@ else {
|
||||
$user{'nochange'},":",
|
||||
$user{'temppass'},":",
|
||||
$user{'twofactor_provider'},":",
|
||||
$user{'twofactor_id'},
|
||||
$user{'twofactor_id'},":",
|
||||
$user{'twofactor_apikey'},
|
||||
"\n");
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -458,7 +458,7 @@ schema_download=Download schema file : <a href=$1>$1</a>
|
||||
|
||||
twofactor_err=Failed to setup two-factor authentication
|
||||
twofactor_euser=Your Webmin user was not found!
|
||||
twofactor_title=Two-Factor Authentication
|
||||
twofactor_title=Your Two-Factor Authentication
|
||||
twofactor_disable=Disable Two-Factor Authentication
|
||||
twofactor_already=Your Webmin login already has two-factor authentication enabled with provider $1 and account ID $2.
|
||||
twofactor_desc=This page allows you to enable two-factor authentication for your Webmin login using <a href='$2' target=_blank>$1</a>. Once active, an additional authentication token will be required when logging into Webmin.
|
||||
|
||||
@@ -47,6 +47,7 @@ elsif ($in{'disable'}) {
|
||||
# Turn off for this user
|
||||
$user->{'twofactor_provider'} = undef;
|
||||
$user->{'twofactor_id'} = undef;
|
||||
$user->{'twofactor_apikey'} = undef;
|
||||
&modify_user($user->{'name'}, $user);
|
||||
&reload_miniserv();
|
||||
&webmin_log("onefactor", "user", $user->{'name'});
|
||||
|
||||
@@ -328,6 +328,7 @@ $user{'temppass'} = $in{'temp'};
|
||||
if ($in{'cancel'}) {
|
||||
$user->{'twofactor_provider'} = undef;
|
||||
$user->{'twofactor_id'} = undef;
|
||||
$user->{'twofactor_apikey'} = undef;
|
||||
}
|
||||
|
||||
if ($in{'old'}) {
|
||||
|
||||
@@ -12,13 +12,13 @@ require './acl-lib.pl';
|
||||
$module_name eq 'acl' || die "Command must be run with full path";
|
||||
|
||||
# Check command-line args
|
||||
@ARGV == 4 || die "Usage: $0 user provider id token";
|
||||
($user, $provider, $id, $token) = @ARGV;
|
||||
@ARGV == 5 || die "Usage: $0 user provider id token api-key";
|
||||
($user, $provider, $id, $token, $apikey) = @ARGV;
|
||||
|
||||
# Call the provider validation function
|
||||
&foreign_require("webmin");
|
||||
$func = "webmin::validate_twofactor_".$provider;
|
||||
$err = &$func($id, $token);
|
||||
$err = &$func($id, $token, $apikey);
|
||||
if ($err) {
|
||||
$err =~ s/\r|\n/ /g;
|
||||
print $err,"\n";
|
||||
|
||||
@@ -4477,7 +4477,8 @@ if ($config{'userfile'}) {
|
||||
$temppass{$user[0]} = $user[10];
|
||||
if ($user[11] && $user[12]) {
|
||||
$twofactor{$user[0]} = { 'provider' => $user[11],
|
||||
'id' => $user[12] };
|
||||
'id' => $user[12],
|
||||
'apikey' => $user[13] };
|
||||
}
|
||||
}
|
||||
close(USERS);
|
||||
@@ -5928,7 +5929,8 @@ my $tf = $twofactor{$user};
|
||||
$tf || return undef;
|
||||
pipe(TOKENr, TOKENw);
|
||||
my $pid = &execute_webmin_command($config{'twofactor_wrapper'},
|
||||
[ $user, $tf->{'provider'}, $tf->{'id'}, $token ], TOKENw);
|
||||
[ $user, $tf->{'provider'}, $tf->{'id'}, $token, $tf->{'apikey'} ],
|
||||
TOKENw);
|
||||
close(TOKENw);
|
||||
waitpid($pid, 0);
|
||||
my $ex = $?;
|
||||
|
||||
@@ -4,29 +4,32 @@
|
||||
require './webmin-lib.pl';
|
||||
&ReadParse();
|
||||
&error_setup($text{'twofactor_err'});
|
||||
&get_miniserv_config(\%miniserv);
|
||||
|
||||
# Validate inputs
|
||||
if ($in{'twofactor_provider'}) {
|
||||
($got) = grep { $_->[0] eq $in{'twofactor_provider'} }
|
||||
&list_twofactor_providers();
|
||||
$got || &error($text{'twofactor_eprovider'});
|
||||
$in{'twofactor_apikey'} =~ /^\S+$/ ||
|
||||
&error($text{'twofactor_eapikey'});
|
||||
($prov) = grep { $_->[0] eq $in{'twofactor_provider'} }
|
||||
&list_twofactor_providers();
|
||||
$prov || &error($text{'twofactor_eprovider'});
|
||||
$vfunc = "validate_twofactor_apikey_".$in{'twofactor_provider'};
|
||||
$err = defined(&$vfunc) && &$vfunc($in{'twofactor_apikey'},
|
||||
$in{'twofactor_test'});
|
||||
$err = defined(&$vfunc) && &$vfunc(\%in, \%miniserv);
|
||||
&error($err) if ($err);
|
||||
}
|
||||
|
||||
# Save settings
|
||||
&lock_file($ENV{'MINISERV_CONFIG'});
|
||||
&get_miniserv_config(\%miniserv);
|
||||
$miniserv{'twofactor_provider'} = $in{'twofactor_provider'};
|
||||
$miniserv{'twofactor_apikey'} = $in{'twofactor_apikey'};
|
||||
$miniserv{'twofactor_test'} = $in{'twofactor_test'};
|
||||
|
||||
&put_miniserv_config(\%miniserv);
|
||||
&unlock_file($ENV{'MINISERV_CONFIG'});
|
||||
|
||||
&show_restart_page();
|
||||
$msg = $text{'restart_done'}."<p>\n";
|
||||
if ($in{'twofactor_provider'}) {
|
||||
$msg .= &text('twofactor_enrolllink',
|
||||
"../acl/twofactor_form.cgi")."<p>\n";
|
||||
if ($prov->[2]) {
|
||||
$msg .= &text('twofactor_url', $prov->[1], $prov->[2])."<p>\n";
|
||||
}
|
||||
}
|
||||
&show_restart_page($text{'twofactor_title'}, $msg);
|
||||
|
||||
&webmin_log("twofactor", undef, undef, \%in);
|
||||
|
||||
@@ -2,28 +2,49 @@
|
||||
# Show two-factor authentication options
|
||||
|
||||
require './webmin-lib.pl';
|
||||
ui_print_header(undef, $text{'twofactor_title'}, "");
|
||||
ui_print_header(undef, $text{'twofactor_title'}, "", "twofactor");
|
||||
get_miniserv_config(\%miniserv);
|
||||
|
||||
print "$text{'twofactor_desc'}<p>\n";
|
||||
|
||||
print <<EOF;
|
||||
<style>
|
||||
.opener_shown {display:inline}
|
||||
.opener_hidden {display:none}
|
||||
</style>
|
||||
EOF
|
||||
|
||||
@provs = &list_twofactor_providers();
|
||||
print "<script>\n";
|
||||
print "function show_prov(name) {\n";
|
||||
foreach $p (@provs) {
|
||||
print "d = document.getElementById(\"$p->[0]\");\n";
|
||||
print "d.className = name == \"$p->[0]\" ? \"opener_shown\" : \"opener_hidden\";\n";
|
||||
}
|
||||
print "}\n";
|
||||
print "</script>\n";
|
||||
|
||||
print ui_form_start("change_twofactor.cgi", "post");
|
||||
print ui_table_start($text{'twofactor_header'}, undef, 2);
|
||||
|
||||
# Two-factor provider
|
||||
my ($name, $value, $opts, $size, $multiple, $missing, $dis, $js) = @_;
|
||||
print ui_table_row($text{'twofactor_provider'},
|
||||
ui_select("twofactor_provider", $miniserv{'twofactor_provider'},
|
||||
[ [ "", "<".$text{'twofactor_none'}.">" ],
|
||||
map { [ $_->[0], $_->[1]." - ".$_->[2] ] }
|
||||
&list_twofactor_providers() ]));
|
||||
map { [ $_->[0], $_->[1] ] } @provs ],
|
||||
1, 0, 0, 0, "onChange='show_prov(value)'"));
|
||||
|
||||
# API key
|
||||
print ui_table_row($text{'twofactor_apikey'},
|
||||
ui_textbox("twofactor_apikey", $miniserv{'twofactor_apikey'}, 40));
|
||||
|
||||
# Test mode?
|
||||
print ui_table_row($text{'twofactor_test'},
|
||||
ui_yesno_radio("twofactor_test", $miniserv{'twofactor_test'}));
|
||||
foreach $p (@provs) {
|
||||
$dis = $p->[0] eq $miniserv{'twofactor_provider'} ? "opener_shown"
|
||||
: "opener_hidden";
|
||||
print "<div id=$p->[0] name=$p->[0] class='$dis'>\n";
|
||||
$sfunc = "show_twofactor_apikey_".$p->[0];
|
||||
if (defined(&$sfunc)) {
|
||||
print &$sfunc(\%miniserv);
|
||||
}
|
||||
print "</div>\n";
|
||||
}
|
||||
|
||||
print ui_table_end();
|
||||
print ui_form_end([ [ "save", $text{'save'} ] ]);
|
||||
|
||||
@@ -1025,13 +1025,14 @@ twofactor_title=Two-Factor Authentication
|
||||
twofactor_header=Two-factor authentication sessions
|
||||
twofactor_provider=Authentication provider
|
||||
twofactor_none=None
|
||||
twofactor_apikey=Provider API key
|
||||
twofactor_apikey=Authy API key
|
||||
twofactor_test=Use provider's test mode?
|
||||
twofactor_desc=Two-factor authentication allows Webmin users to enable use of an additional authentication device when logging in, such as a one-time passcode generator. Users must individually enroll with the selected authentication provider after it is enabled on this page.
|
||||
twofactor_err=Failed to save two-factor authentication
|
||||
twofactor_eprovider=Invalid provider!
|
||||
twofactor_eapikey=Missing or invalid-looking API key
|
||||
twofactor_authy=Authy
|
||||
twofactor_topt=Google Authenticator
|
||||
twofactor_email=Your email address
|
||||
twofactor_country=Cellphone country code
|
||||
twofactor_phone=Cellphone phone number
|
||||
@@ -1044,5 +1045,7 @@ twofactor_eauthyenroll=Enrollment failed : $1
|
||||
twofactor_eauthyid=Authy user ID must be a number
|
||||
twofactor_eauthytoken=Authy token must be a number
|
||||
twofactor_eauthyotp=Authy token is invalid
|
||||
twofactor_enrolllink=You can now enroll for two-factor authentication in the <a href='$1'>Webmin Users</a> module.
|
||||
twofactor_url=To learn more about $1, see it's website at <a href='$2' target=_blank>$2</a>.
|
||||
|
||||
__norefs=1
|
||||
|
||||
@@ -2168,15 +2168,32 @@ if ($tryerror) {
|
||||
# containing an ID, name and URL for more info
|
||||
sub list_twofactor_providers
|
||||
{
|
||||
return ( [ 'authy', $text{'twofactor_authy'},
|
||||
return ( [ 'topt', $text{'twofactor_topt'},
|
||||
'http://en.wikipedia.org/wiki/Google_Authenticator' ],
|
||||
[ 'authy', $text{'twofactor_authy'},
|
||||
'http://www.authy.com/' ] );
|
||||
}
|
||||
|
||||
# validate_twofactor_apikey_authy(apikey, test-mode)
|
||||
# Check that an API key is valid
|
||||
# show_twofactor_apikey_authy(&miniserv)
|
||||
# Returns HTML for the form for authy-specific provider inputs
|
||||
sub show_twofactor_apikey_authy
|
||||
{
|
||||
my ($miniserv) = @_;
|
||||
my $rv;
|
||||
$rv .= ui_table_row($text{'twofactor_apikey'},
|
||||
ui_textbox("authy_apikey", $miniserv->{'twofactor_apikey'}, 40));
|
||||
return $rv;
|
||||
}
|
||||
|
||||
# validate_twofactor_apikey_authy(&in, &miniserv)
|
||||
# Validates inputs from show_twofactor_apikey_authy, and stores them. Returns undef
|
||||
# if OK, or an error message on failure
|
||||
sub validate_twofactor_apikey_authy
|
||||
{
|
||||
my ($key, $test) = @_;
|
||||
my ($in, $miniserv) = @_;
|
||||
my $key = $in->{'authy_apikey'};
|
||||
my $test = $miniserv->{'twofactor_test'};
|
||||
$key =~ /^\S+$/ || return $text{'twofactor_eapikey'};
|
||||
my $host = $test ? "sandbox-api.authy.com" : "api.authy.com";
|
||||
my $port = $test ? 80 : 443;
|
||||
my $page = "/protected/xml/app/details?api_key=".&urlize($key);
|
||||
@@ -2190,6 +2207,7 @@ if ($err =~ /401/) {
|
||||
elsif ($err) {
|
||||
return &text('twofactor_eauthy', $err);
|
||||
}
|
||||
$miniserv->{'twofactor_apikey'} = $key;
|
||||
return undef;
|
||||
}
|
||||
|
||||
@@ -2246,6 +2264,7 @@ my ($out, $err);
|
||||
return $err if ($err);
|
||||
if ($out =~ /<id[^>]*>([^<]+)<\/id>/i) {
|
||||
$user->{'twofactor_id'} = $1;
|
||||
$user->{'twofactor_apikey'} = $miniserv{'twofactor_apikey'};
|
||||
return undef;
|
||||
}
|
||||
else {
|
||||
@@ -2254,11 +2273,11 @@ else {
|
||||
}
|
||||
}
|
||||
|
||||
# validate_twofactor_authy(id, token)
|
||||
# validate_twofactor_authy(id, token, apikey)
|
||||
# Checks the validity of some token for a user ID
|
||||
sub validate_twofactor_authy
|
||||
{
|
||||
my ($id, $token) = @_;
|
||||
my ($id, $token, $apikey) = @_;
|
||||
$id =~ /^\d+$/ || return $text{'twofactor_eauthyid'};
|
||||
$token =~ /^\d+$/ || return $text{'twofactor_eauthytoken'};
|
||||
my %miniserv;
|
||||
@@ -2266,8 +2285,8 @@ my %miniserv;
|
||||
my $host = $miniserv{'twofactor_test'} ? "sandbox-api.authy.com"
|
||||
: "api.authy.com";
|
||||
my $port = $miniserv{'twofactor_test'} ? 80 : 443;
|
||||
my $page = "/protected/xml/verify/$token/$id?api_key=".
|
||||
&urlize($miniserv{'twofactor_apikey'});
|
||||
my $page = "/protected/xml/verify/$token/$id?api_key=".&urlize($apikey).
|
||||
"&force=true";
|
||||
my $ssl = $miniserv{'twofactor_test'} ? 0 : 1;
|
||||
my ($out, $err);
|
||||
&http_download($host, $port, $page, \$out, \$err, undef, $ssl, undef, undef,
|
||||
@@ -2300,4 +2319,17 @@ else {
|
||||
}
|
||||
}
|
||||
|
||||
# validate_twofactor_apikey_topt()
|
||||
# Checks that the needed Perl module for TOPT is installed
|
||||
sub validate_twofactor_apikey_topt
|
||||
{
|
||||
# XXX
|
||||
}
|
||||
|
||||
# show_twofactor_form_topt()
|
||||
# XXX is this even needed?
|
||||
sub show_twofactor_form_topt
|
||||
{
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
Reference in New Issue
Block a user