# Functions for two-factor enrollment and verification # list_twofactor_providers() # Returns a list of all supported providers, each of which is an array ref # containing an ID, name and URL for more info sub list_twofactor_providers { return ( [ 'totp', 'Google Authenticator', 'http://en.wikipedia.org/wiki/Google_Authenticator' ], [ 'authy', 'Authy', 'http://www.authy.com/' ] ); } # 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 ($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); my $ssl = $test ? 0 : 1; my ($out, $err); &http_download($host, $port, $page, \$out, \$err, undef, $ssl, undef, undef, 60, 0, 1); if ($err =~ /401/) { return $text{'twofactor_eauthykey'}; } elsif ($err) { return &text('twofactor_eauthy', $err); } $miniserv->{'twofactor_apikey'} = $key; return undef; } # show_twofactor_form_authy(&webmin-user) # Returns HTML for a form for enrolling for Authy two-factor sub show_twofactor_form_authy { my ($user) = @_; my $rv; $rv .= &ui_table_row($text{'twofactor_email'}, &ui_textbox("email", undef, 40)); $rv .= &ui_table_row($text{'twofactor_country'}, &ui_textbox("country", undef, 3)); $rv .= &ui_table_row($text{'twofactor_phone'}, &ui_textbox("phone", undef, 20)); return $rv; } # parse_twofactor_form_authy(&in, &user) # Parses inputs from show_twofactor_form_authy, and returns a hash ref with # enrollment details on success, or an error message on failure. sub parse_twofactor_form_authy { my ($in, $user) = @_; $in->{'email'} =~ /^\S+\@\S+$/ || return $text{'twofactor_eemail'}; $in->{'country'} =~ s/^\+//; $in->{'country'} =~ /^\d{1,3}$/ || return $text{'twofactor_ecountry'}; $in->{'phone'} =~ /^[0-9\- ]+$/ || return $text{'twofactor_ephone'}; return { 'email' => $in->{'email'}, 'country' => $in->{'country'}, 'phone' => $in->{'phone'} }; } # enroll_twofactor_authy(&details, &user) # Attempts to enroll a user for Authy two-factor. Returns undef on success and # sets twofactor_id in &user, or an error message on failure. sub enroll_twofactor_authy { my ($details, $user) = @_; my %miniserv; &get_miniserv_config(\%miniserv); my $host = $miniserv{'twofactor_test'} ? "sandbox-api.authy.com" : "api.authy.com"; my $port = $miniserv{'twofactor_test'} ? 80 : 443; my $page = "/protected/xml/users/new?api_key=". &urlize($miniserv{'twofactor_apikey'}); my $ssl = $miniserv{'twofactor_test'} ? 0 : 1; my $content = "user[email]=".&urlize($details->{'email'})."&". "user[country_code]=".&urlize($details->{'country'})."&". "user[cellphone]=".&urlize($details->{'phone'}); my ($out, $err); &http_post($host, $port, $page, $content, \$out, \$err, undef, $ssl, undef, undef, 60, 0, 1); return $err if ($err); if ($out =~ /<id[^>]*>([^<]+)<\/id>/i) { $user->{'twofactor_id'} = $1; $user->{'twofactor_apikey'} = $miniserv{'twofactor_apikey'}; return undef; } else { return &text('twofactor_eauthyenroll', "<pre>".&html_escape($out)."</pre>"); } } # validate_twofactor_authy(id, token, apikey) # Checks the validity of some token for a user ID sub validate_twofactor_authy { my ($id, $token, $apikey) = @_; $id =~ /^\d+$/ || return $text{'twofactor_eauthyid'}; $token =~ /^\d+$/ || return $text{'twofactor_eauthytoken'}; my %miniserv; &get_miniserv_config(\%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($apikey). "&force=true"; my $ssl = $miniserv{'twofactor_test'} ? 0 : 1; my ($out, $err); &http_download($host, $port, $page, \$out, \$err, undef, $ssl, undef, undef, 60, 0, 1); if ($err && $err =~ /401/) { # Token rejected return $text{'twofactor_eauthyotp'}; } elsif ($err) { # Some other error return $err; } elsif ($out && $out =~ /<success[^>]*>([^<]+)<\/success>/i) { if (lc($1) eq "true") { # Worked! return undef; } elsif ($out =~ /<message[^>]*>([^<]+)<\/message>/i) { # Failed, but with a message return $1; } else { # Failed, not sure why return $out; } } else { # Unknown output return $out; } } # validate_twofactor_apikey_totp() # Checks that the needed Perl module for TOTP is installed. sub validate_twofactor_apikey_totp { my ($miniserv, $in) = @_; eval "use Authen::OATH"; if ($@) { return &text('twofactor_etotpmodule', 'Authen::OATH', "../cpan/download.cgi?source=3&cpan=Authen::OATH&mode=2&". "return=/$module_name/&returndesc=".&urlize($text{'index_return'})) } return undef; } # show_twofactor_form_totp(&user) # Show form allowing the user to choose a twofactor secret sub show_twofactor_form_totp { my ($user) = @_; my $secret = $user->{'twofactor_id'}; $secret = undef if ($secret !~ /^[A-Z0-9=]+$/i || (length($secret) != 16 && length($secret) != 26 && length($secret) != 32)); my $rv; $rv .= &ui_table_row($text{'twofactor_secret'}, &ui_opt_textbox("totp_secret", $secret, 20, $text{'twofactor_secret1'}, $text{'twofactor_secret0'})); return $rv; } # parse_twofactor_form_totp(&in, &user) # Generate or use a secret key for this user sub parse_twofactor_form_totp { my ($in, $user) = @_; if ($in->{'totp_secret_def'}) { $user->{'twofactor_id'} = &encode_base32(&generate_base32_secret()); } else { $in{'totp_secret'} =~ /^[A-Z0-9=]{16}$/i || return $text{'twofactor_esecret'}; $user->{'twofactor_id'} = $in{'totp_secret'}; } return { }; } # generate_base32_secret([length]) # Returns a base-32 encoded secret of by default 10 bytes sub generate_base32_secret { my ($length) = @_; $length ||= 10; &seed_random(); my $secret = ""; while(length($secret) < $length) { $secret .= chr(rand()*256); } return $secret; } # enroll_twofactor_totp(&in, &user) # Generate a secret for this user, based-32 encoded sub enroll_twofactor_totp { my ($in, $user) = @_; $user->{'twofactor_id'} ||= &encode_base32(&generate_base32_secret()); return undef; } # message_twofactor_totp(&user) # Returns HTML to display after a user enrolls sub message_twofactor_totp { my ($user) = @_; my $name = &urlize(&get_display_hostname() . " (" . $user->{'name'} . ")"); my $url = "https://chart.googleapis.com/chart". "?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/". $name."%3Fsecret%3D".$user->{'twofactor_id'}; my $rv; $rv .= &text('twofactor_qrcode', "<tt>$user->{'twofactor_id'}</tt>")."<p>\n"; $rv .= "<img src='$url' border=0><p>\n"; return $rv; } # validate_twofactor_totp(id, token, apikey) # Checks the validity of some token with google authenticator sub validate_twofactor_totp { my ($id, $token, $apikey) = @_; $id =~ /^[A-Z0-9=]+$/i || return $text{'twofactor_etotpid'}; $token =~ /^\d+$/ || return $text{'twofactor_etotptoken'}; eval "use Authen::OATH"; if ($@) { return &text('twofactor_etotpmodule2', 'Authen::OATH'); } my $secret = &decode_base32($id); my $oauth = Authen::OATH->new(); my $now = time(); foreach my $t ($now - 30, $now, $now + 30) { my $expected = $oauth->totp($secret, $t); return undef if ($expected eq $token); } return $text{'twofactor_etotpmatch'}; } # get_user_twofactor(username, &miniserv) # Returns the twofactor provider, ID and API key for a user sub get_user_twofactor { my ($user, $miniserv) = @_; return () if (!$miniserv->{'twofactorfile'}); my $lref = &read_file_lines($miniserv->{'twofactorfile'}, 1); foreach my $l (@$lref) { my @two = split(/:/, $l, -1); if ($two[0] eq $user) { return ($two[1], $two[2], $two[3]); } } return (); } # save_user_twofactor(username, &miniserv, [provider, id, api-key]) # Updates or removes the twofactor provider for a user sub save_user_twofactor { my ($user, $miniserv, $prov, $id, $key) = @_; return 0 if (!$miniserv->{'twofactorfile'}); &lock_file($miniserv->{'twofactorfile'}); my $lref = &read_file_lines($miniserv->{'twofactorfile'}); my $found = 0; my $i = 0; foreach my $l (@$lref) { my @two = split(/:/, $l, -1); if ($two[0] eq $user) { # Found the line to update or remove if ($prov) { $lref->[$i] = join(":", $user, $prov, $id, $key); } else { splice(@$lref, $i, 1); } $found++; last; } $i++; } if (!$found && $prov) { # Need to add the user push(@$lref, join(":", $user, $prov, $id, $key)); } &flush_file_lines($miniserv->{'twofactorfile'}); &unlock_file($miniserv->{'twofactorfile'}); } 1;
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
help | Folder | 0755 |
|
|
images | Folder | 0755 |
|
|
lang | Folder | 0755 |
|
|
subdir | Folder | 0755 |
|
|
CHANGELOG | File | 12.02 KB | 0644 |
|
acme_tiny.py | File | 11.24 KB | 0755 |
|
adminupgrade | File | 299 B | 0644 |
|
backup_config.pl | File | 1.97 KB | 0755 |
|
bootup.cgi | File | 1.04 KB | 0755 |
|
cache.cgi | File | 1.47 KB | 0755 |
|
cgi_args.pl | File | 159 B | 0755 |
|
change_access.cgi | File | 1.34 KB | 0755 |
|
change_advanced.cgi | File | 3 KB | 0755 |
|
change_anon.cgi | File | 712 B | 0755 |
|
change_bind.cgi | File | 4.8 KB | 0755 |
|
change_ca.cgi | File | 674 B | 0755 |
|
change_debug.cgi | File | 1.38 KB | 0755 |
|
change_lang.cgi | File | 558 B | 0755 |
|
change_lock.cgi | File | 554 B | 0755 |
|
change_log.cgi | File | 2.38 KB | 0755 |
|
change_mobile.cgi | File | 940 B | 0755 |
|
change_os.cgi | File | 1.85 KB | 0755 |
|
change_osdn.cgi | File | 1.54 KB | 0755 |
|
change_overlay.cgi | File | 1.16 KB | 0755 |
|
change_proxy.cgi | File | 1.09 KB | 0755 |
|
change_referers.cgi | File | 633 B | 0755 |
|
change_session.cgi | File | 4.77 KB | 0755 |
|
change_ssl.cgi | File | 3.03 KB | 0755 |
|
change_startpage.cgi | File | 773 B | 0755 |
|
change_status.cgi | File | 1.02 KB | 0755 |
|
change_theme.cgi | File | 1.16 KB | 0755 |
|
change_twofactor.cgi | File | 1.43 KB | 0755 |
|
change_ui.cgi | File | 1.61 KB | 0755 |
|
change_web.cgi | File | 2.47 KB | 0755 |
|
clear_blocked.cgi | File | 154 B | 0755 |
|
clear_cache.cgi | File | 205 B | 0755 |
|
clone_mod.cgi | File | 2.06 KB | 0755 |
|
config | File | 114 B | 0644 |
|
config.info | File | 696 B | 0644 |
|
config.info.ar | File | 414 B | 0644 |
|
config.info.ca | File | 408 B | 0644 |
|
config.info.cs | File | 233 B | 0644 |
|
config.info.de | File | 368 B | 0644 |
|
config.info.es | File | 229 B | 0644 |
|
config.info.fa | File | 301 B | 0644 |
|
config.info.fr | File | 577 B | 0644 |
|
config.info.hr | File | 0 B | 0644 |
|
config.info.hu | File | 0 B | 0644 |
|
config.info.it | File | 245 B | 0644 |
|
config.info.ja | File | 531 B | 0644 |
|
config.info.ko | File | 206 B | 0644 |
|
config.info.ms | File | 286 B | 0644 |
|
config.info.nl | File | 299 B | 0644 |
|
config.info.no | File | 283 B | 0644 |
|
config.info.pl | File | 284 B | 0644 |
|
config.info.pt_BR | File | 299 B | 0644 |
|
config.info.ru | File | 491 B | 0644 |
|
config.info.sk | File | 132 B | 0644 |
|
config.info.sv | File | 202 B | 0644 |
|
config.info.tr | File | 155 B | 0644 |
|
cpan_modules.pl | File | 229 B | 0755 |
|
defaultacl | File | 17 B | 0644 |
|
delete_cache.cgi | File | 471 B | 0755 |
|
delete_mod.cgi | File | 2.24 KB | 0755 |
|
delete_webmincron.cgi | File | 1.51 KB | 0755 |
|
download_cert.cgi | File | 532 B | 0755 |
|
edit_access.cgi | File | 1.38 KB | 0755 |
|
edit_advanced.cgi | File | 3.87 KB | 0755 |
|
edit_anon.cgi | File | 812 B | 0755 |
|
edit_assignment.cgi | File | 1.12 KB | 0755 |
|
edit_bind.cgi | File | 2.95 KB | 0755 |
|
edit_blocked.cgi | File | 944 B | 0755 |
|
edit_ca.cgi | File | 2.82 KB | 0755 |
|
edit_categories.cgi | File | 1.69 KB | 0755 |
|
edit_debug.cgi | File | 2.04 KB | 0755 |
|
edit_descs.cgi | File | 1.49 KB | 0755 |
|
edit_ipkey.cgi | File | 1.7 KB | 0755 |
|
edit_lang.cgi | File | 1.82 KB | 0755 |
|
edit_lock.cgi | File | 763 B | 0755 |
|
edit_log.cgi | File | 3.04 KB | 0755 |
|
edit_mobile.cgi | File | 1.26 KB | 0755 |
|
edit_mods.cgi | File | 4.45 KB | 0755 |
|
edit_os.cgi | File | 2.72 KB | 0755 |
|
edit_proxy.cgi | File | 3.7 KB | 0755 |
|
edit_referers.cgi | File | 899 B | 0755 |
|
edit_sendmail.cgi | File | 3.48 KB | 0755 |
|
edit_session.cgi | File | 5.18 KB | 0755 |
|
edit_ssl.cgi | File | 10.55 KB | 0755 |
|
edit_startpage.cgi | File | 1.68 KB | 0755 |
|
edit_status.cgi | File | 1.13 KB | 0755 |
|
edit_themes.cgi | File | 3.72 KB | 0755 |
|
edit_twofactor.cgi | File | 1.5 KB | 0755 |
|
edit_ui.cgi | File | 2.51 KB | 0755 |
|
edit_upgrade.cgi | File | 4.26 KB | 0755 |
|
edit_web.cgi | File | 2.88 KB | 0755 |
|
edit_webmincron.cgi | File | 1.35 KB | 0755 |
|
export_mod.cgi | File | 1.23 KB | 0755 |
|
feedback_files.pl | File | 126 B | 0755 |
|
fix_os.cgi | File | 228 B | 0755 |
|
gnupg-lib.pl | File | 13.38 KB | 0755 |
|
hide.cgi | File | 326 B | 0755 |
|
index.cgi | File | 4.16 KB | 0755 |
|
install_mod.cgi | File | 3.11 KB | 0755 |
|
install_theme.cgi | File | 2.29 KB | 0755 |
|
jcameron-key.asc | File | 1.29 KB | 0644 |
|
letsencrypt-cleanup.pl | File | 2.02 KB | 0755 |
|
letsencrypt-dns.pl | File | 2.57 KB | 0755 |
|
letsencrypt-lib.pl | File | 14.03 KB | 0755 |
|
letsencrypt.cgi | File | 4.64 KB | 0755 |
|
log_parser.pl | File | 1.23 KB | 0755 |
|
module.info | File | 195 B | 0644 |
|
module.info.af | File | 0 B | 0644 |
|
module.info.af.auto | File | 142 B | 0644 |
|
module.info.ar | File | 185 B | 0644 |
|
module.info.ar.auto | File | 22 B | 0644 |
|
module.info.be | File | 0 B | 0644 |
|
module.info.be.auto | File | 208 B | 0644 |
|
module.info.bg | File | 0 B | 0644 |
|
module.info.bg.auto | File | 218 B | 0644 |
|
module.info.ca | File | 134 B | 0644 |
|
module.info.ca.auto | File | 15 B | 0644 |
|
module.info.cs | File | 28 B | 0644 |
|
module.info.cs.auto | File | 128 B | 0644 |
|
module.info.da | File | 0 B | 0644 |
|
module.info.da.auto | File | 142 B | 0644 |
|
module.info.de | File | 126 B | 0644 |
|
module.info.de.auto | File | 15 B | 0644 |
|
module.info.el | File | 0 B | 0644 |
|
module.info.el.auto | File | 262 B | 0644 |
|
module.info.es | File | 33 B | 0644 |
|
module.info.es.auto | File | 109 B | 0644 |
|
module.info.eu | File | 0 B | 0644 |
|
module.info.eu.auto | File | 158 B | 0644 |
|
module.info.fa | File | 0 B | 0644 |
|
module.info.fa.auto | File | 202 B | 0644 |
|
module.info.fi | File | 0 B | 0644 |
|
module.info.fi.auto | File | 141 B | 0644 |
|
module.info.fr | File | 32 B | 0644 |
|
module.info.fr.auto | File | 129 B | 0644 |
|
module.info.he | File | 0 B | 0644 |
|
module.info.he.auto | File | 195 B | 0644 |
|
module.info.hr | File | 0 B | 0644 |
|
module.info.hr.auto | File | 149 B | 0644 |
|
module.info.hu | File | 30 B | 0644 |
|
module.info.hu.auto | File | 148 B | 0644 |
|
module.info.it | File | 33 B | 0644 |
|
module.info.it.auto | File | 107 B | 0644 |
|
module.info.ja | File | 180 B | 0644 |
|
module.info.ko | File | 22 B | 0644 |
|
module.info.ko.auto | File | 129 B | 0644 |
|
module.info.lt | File | 0 B | 0644 |
|
module.info.lt.auto | File | 180 B | 0644 |
|
module.info.lv | File | 0 B | 0644 |
|
module.info.lv.auto | File | 157 B | 0644 |
|
module.info.ms | File | 119 B | 0644 |
|
module.info.ms.auto | File | 15 B | 0644 |
|
module.info.mt | File | 0 B | 0644 |
|
module.info.mt.auto | File | 144 B | 0644 |
|
module.info.nl | File | 28 B | 0644 |
|
module.info.nl.auto | File | 117 B | 0644 |
|
module.info.no | File | 29 B | 0644 |
|
module.info.no.auto | File | 117 B | 0644 |
|
module.info.pl | File | 155 B | 0644 |
|
module.info.pl.auto | File | 15 B | 0644 |
|
module.info.pt | File | 33 B | 0644 |
|
module.info.pt.auto | File | 113 B | 0644 |
|
module.info.pt_BR | File | 36 B | 0644 |
|
module.info.pt_BR.auto | File | 119 B | 0644 |
|
module.info.ro | File | 0 B | 0644 |
|
module.info.ro.auto | File | 147 B | 0644 |
|
module.info.ru | File | 34 B | 0644 |
|
module.info.ru.auto | File | 172 B | 0644 |
|
module.info.sk | File | 30 B | 0644 |
|
module.info.sk.auto | File | 132 B | 0644 |
|
module.info.sl | File | 0 B | 0644 |
|
module.info.sl.auto | File | 147 B | 0644 |
|
module.info.sv | File | 30 B | 0644 |
|
module.info.sv.auto | File | 114 B | 0644 |
|
module.info.th | File | 0 B | 0644 |
|
module.info.th.auto | File | 258 B | 0644 |
|
module.info.tr | File | 33 B | 0644 |
|
module.info.tr.auto | File | 128 B | 0644 |
|
module.info.uk | File | 0 B | 0644 |
|
module.info.uk.auto | File | 215 B | 0644 |
|
module.info.ur | File | 0 B | 0644 |
|
module.info.ur.auto | File | 209 B | 0644 |
|
module.info.vi | File | 0 B | 0644 |
|
module.info.vi.auto | File | 177 B | 0644 |
|
module.info.zh | File | 22 B | 0644 |
|
module.info.zh_TW | File | 25 B | 0644 |
|
module.info.zh_TW.auto | File | 115 B | 0644 |
|
newcsr.cgi | File | 800 B | 0755 |
|
newkey.cgi | File | 879 B | 0755 |
|
postinstall.pl | File | 2.01 KB | 0755 |
|
refresh_modules.cgi | File | 664 B | 0755 |
|
restart.cgi | File | 87 B | 0755 |
|
save_assignment.cgi | File | 485 B | 0755 |
|
save_categories.cgi | File | 946 B | 0755 |
|
save_descs.cgi | File | 1006 B | 0755 |
|
save_ipkey.cgi | File | 1.31 KB | 0755 |
|
save_newmod.cgi | File | 278 B | 0755 |
|
save_sendmail.cgi | File | 2.08 KB | 0755 |
|
save_webmincron.cgi | File | 1016 B | 0755 |
|
savekey.cgi | File | 2.8 KB | 0755 |
|
setup_ca.cgi | File | 1.52 KB | 0755 |
|
standard_chooser.cgi | File | 1.68 KB | 0755 |
|
stop_ca.cgi | File | 1.03 KB | 0755 |
|
syslog_logs.pl | File | 633 B | 0755 |
|
system_info.pl | File | 5.02 KB | 0644 |
|
test_sendmail.cgi | File | 784 B | 0755 |
|
third_chooser.cgi | File | 1.55 KB | 0755 |
|
twofactor-funcs-lib.pl | File | 8.81 KB | 0644 |
|
uninstall.pl | File | 236 B | 0755 |
|
update.cgi | File | 2.86 KB | 0755 |
|
upgrade.cgi | File | 16.6 KB | 0755 |
|
view_webmincron.cgi | File | 1.66 KB | 0755 |
|
webmin-lib.pl | File | 68.77 KB | 0755 |
|