├── LICENSE ├── examples └── authorized_keys_script.rb ├── README.textile ├── openssh-5.9p1-script-auth.diff └── openssh-5.8p1-script-auth.diff /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009, Jonathan Dance 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of Norbauer, Inc, nor Wuputah, LLC, nor the names of its 13 | contributors may be used to endorse or promote products derived from this 14 | software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /examples/authorized_keys_script.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | # logging is inefficient - recommended to use for testing only 4 | LOGGING = true 5 | LOGFILE = File.join(File.dirname(__FILE__), 'authorized_script_log') 6 | 7 | # if we're logging, set up log file in advance 8 | if LOGGING 9 | begin 10 | LOG = File.open(LOGFILE, 'a') 11 | at_exit { LOG.close } 12 | rescue SystemCallError 13 | LOG = nil 14 | end 15 | end 16 | 17 | def log(msg) 18 | # still recommend adding "if LOGGING" to your log statements for performance reasons 19 | LOG.puts "[#{Time.now}] #{msg}" if LOG 20 | end 21 | 22 | user, key = STDIN.read.split("\n") 23 | log("trying to authorize key: #{key}") if LOGGING 24 | 25 | unless key =~ /^ssh-(?:dss|rsa) [A-Za-z0-9+\/]+$/ 26 | log("[ERROR] invalid key!") if LOGGING 27 | Kernel.exit(1) 28 | end 29 | 30 | # Only load mysql if we validated the key 31 | # mysql is a fairly substantial library, and so it may be faster to change 32 | # this to a simple API call to an already running service. 33 | require 'rubygems' 34 | gem 'mysql' 35 | require 'mysql' 36 | 37 | user = nil 38 | begin 39 | mysql = Mysql.connect('localhost', 'username', 'password', 'database') 40 | at_exit { mysql.close } 41 | mysql.query("SELECT username FROM user_keys WHERE key = '#{Mysql.quote(key)}' LIMIT 2") do |result| 42 | case result.num_rows 43 | when 0 44 | log("key not found") if LOGGING 45 | Kernel.exit(1) 46 | when 1 47 | user = result.fetch_row.first 48 | log("user found: #{user}") if LOGGING 49 | when 2 50 | log("[ERROR] Key is not unique in the database!") if LOGGING 51 | Kernel.exit(1) 52 | end 53 | end 54 | rescue Mysql::Error => e 55 | log("[ERROR] #{e.class}: #{e.to_s}") if LOGGING 56 | Kernel.exit(1) 57 | end 58 | 59 | # there is an assumption here is that 'user' is a safe value 60 | STDOUT.print %Q[command="gitosis-serve #{user}",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty] 61 | 62 | Kernel.exit(0) 63 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h1. OpenSSH with script authentication 2 | 3 | This OpenSSH hack allows you to use a custom script to do public key lookup and authentication. 4 | 5 | h2. Auditing 6 | 7 | We welcome audits and improvements to the patch (we're not expert C hackers). The @patched-openssh-5.8p1@ branch is @openssh-5.8p1@ with the patch applied. 8 | 9 | h2. Security 10 | 11 | Are you kidding? *We forked a fork of OpenSSH*. Though we only added a few dozen lines, odds are we also incorporated a few hundred buffer overflow vectors. 12 | If you're going to run this hack, it is recommended you run this in a "@chroot@ jail":http://olivier.sessink.nl/jailkit/jailkit.8.html . 13 | 14 | We *strongly advise against using this patched version of sshd for the main sshd on your server* -- run a normal install of sshd on a non-standard port. 15 | 16 | In summary: *USE THIS PATCH AT YOUR OWN RISK.* 17 | 18 | h2. Setup and Configuration 19 | 20 | In order to do so, we added an optional @AuthorizedKeysScript@ option to your @sshd_config@. The format and functionality is identical to @AuthorizedKeysFile@, except the path will reference a program instead of a flatfile. The value I use is @.ssh/authorized_keys_script@. 21 | 22 | h2. Writing the Program/Script 23 | 24 | The script must follow certain guidelines in order for this to work properly. 25 | 26 | Your program will receive the username and public key on STDIN, seperated by a newline and terminated by EOF. It will be in canoncial SSH public key format, starting with @ssh-dss@ or @ssh-rsa@, a space, and then the key data. 27 | 28 | Your program's exit code is the most important output from your script. *An exit code of 0 means success, while 1 means failure.* Success allows the user to login with that key. It is thus *VERY important* that your script does not blindly exit with a code of 0, or the user will be able to login as the user. 29 | 30 | Your script has the option of outputting ssh options in the same format as @authorized_keys@. These options are printed to standard out. You must print only the options without any whitespace or newlines at the end (i.e., in Ruby, use @print@ not @puts@). A valid option string might be @command="gitosis-serve jd",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty@. Outputting invalid options (or a newline) will result in the user's key being rejected, regardless of your exit code. Additionally, your scripts output will be entirely ignored if the exit code is not 0. 31 | 32 | You should write your script to timeout and exit (with a status code of 1) after some number of seconds, otherwise the sshd session will hang waiting for your script to return. This should not affect the overall sshd daemon, but might leave forked sessions hung, requiring sshd to be restarted. 33 | 34 | h2. To-do 35 | 36 | * SIGCHLD confirmation - is the current handling of SIGCHLD ok? 37 | * Add a timeout to assure the script does not permanently hang the sshd session. If timeout occurs, kill the child process. 38 | * Make the options output handling a bit less sensitive, e.g. handle if a newline is printed. 39 | 40 | h2. License 41 | 42 | The modifications are released under the BSD license. 43 | -------------------------------------------------------------------------------- /openssh-5.9p1-script-auth.diff: -------------------------------------------------------------------------------- 1 | diff --git a/auth.c b/auth.c 2 | index cd95da9..b91b7de 100644 3 | --- a/auth.c 4 | +++ b/auth.c 5 | @@ -355,6 +355,15 @@ expand_authorized_keys(const char *filename, struct passwd *pw) 6 | } 7 | 8 | char * 9 | +authorized_keys_script(struct passwd *pw) 10 | +{ 11 | + if (options.authorized_keys_script) 12 | + return expand_authorized_keys(options.authorized_keys_script, pw); 13 | + else 14 | + return NULL; 15 | +} 16 | + 17 | +char * 18 | authorized_principals_file(struct passwd *pw) 19 | { 20 | if (options.authorized_principals_file == NULL) 21 | diff --git a/auth.h b/auth.h 22 | index 0d786c4..46f245a 100644 23 | --- a/auth.h 24 | +++ b/auth.h 25 | @@ -169,6 +169,7 @@ int verify_response(Authctxt *, const char *); 26 | void abandon_challenge_response(Authctxt *); 27 | 28 | char *expand_authorized_keys(const char *, struct passwd *pw); 29 | +char *authorized_keys_script(struct passwd *); 30 | char *authorized_principals_file(struct passwd *); 31 | 32 | FILE *auth_openkeyfile(const char *, struct passwd *, int); 33 | diff --git a/auth2-pubkey.c b/auth2-pubkey.c 34 | index 137887e..b2680ef 100644 35 | --- a/auth2-pubkey.c 36 | +++ b/auth2-pubkey.c 37 | @@ -254,6 +254,100 @@ match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert) 38 | return 0; 39 | } 40 | 41 | +/* check to see if the script specified by file can authorize the key 42 | + * 43 | + * the script will have the key written to STDIN, which is identical 44 | + * to the normal public key format. 45 | + * 46 | + * the script must exit with either 0 for success or 1 for failure. 47 | + * the script can print login options (if any) to STDOUT. No whitepace should be added 48 | + * to the output. 49 | + * 50 | + * Use with caution: the script can hang sshd. It is recommended you code the script 51 | + * with a timeout set if it cannot determine authenication quickly. 52 | + */ 53 | +static int 54 | +user_key_found_by_script(struct passwd *pw, Key *key, char *file) 55 | +{ 56 | + pid_t pid; 57 | + char line[SSH_MAX_PUBKEY_BYTES]; 58 | + int pipe_in[2]; 59 | + int pipe_out[2]; 60 | + int exit_code = 1; 61 | + int success = 0; 62 | + FILE *f; 63 | + //mysig_t oldsig; 64 | + 65 | + pipe(pipe_in); 66 | + pipe(pipe_out); 67 | + 68 | + //oldsig = signal(SIGCHLD, SIG_IGN); 69 | + temporarily_use_uid(pw); 70 | + 71 | + debug3("user_key_found_by_script: executing %s", file); 72 | + 73 | + switch ((pid = fork())) { 74 | + case -1: 75 | + error("fork(): %s", strerror(errno)); 76 | + restore_uid(); 77 | + return (-1); 78 | + case 0: 79 | + /* setup input pipe */ 80 | + close(pipe_in[1]); 81 | + dup2(pipe_in[0], 0); 82 | + close(pipe_in[0]); 83 | + 84 | + /* setup output pipe */ 85 | + close(pipe_out[0]); 86 | + dup2(pipe_out[1], 1); 87 | + close(pipe_out[1]); 88 | + 89 | + execl(file, file, NULL); 90 | + 91 | + /* exec failed */ 92 | + error("execl(): %s", strerror(errno)); 93 | + _exit(1); 94 | + default: 95 | + debug3("user_key_found_by_script: script pid %d", pid); 96 | + 97 | + close(pipe_in[0]); 98 | + close(pipe_out[1]); 99 | + 100 | + f = fdopen(pipe_in[1], "w"); 101 | + //print the username, a newline, then the provided public key 102 | + fprintf(f, pw->pw_name); 103 | + fprintf(f, "\n"); 104 | + key_write(key, f); 105 | + fclose(f); 106 | + 107 | + while(waitpid(pid, &exit_code, 0) < 0) { 108 | + switch(errno) { 109 | + case EINTR: 110 | + debug3("user_key_found_by_script: waitpid() EINTR, continuing"); 111 | + continue; 112 | + default: 113 | + error("waitpid(): %s", strerror(errno)); 114 | + goto waitpid_error; 115 | + } 116 | + } 117 | + if (WIFEXITED(exit_code) && WEXITSTATUS(exit_code) == 0) { 118 | + int amt_read = read(pipe_out[0], line, sizeof(line) - 1); 119 | + line[amt_read] = ' '; 120 | + line[amt_read + 1] = 0; 121 | + debug3("user_key_found_by_script: options: %s", line); 122 | + if (auth_parse_options(pw, line, file, 0) == 1) 123 | + success = 1; 124 | + } 125 | + waitpid_error: 126 | + close(pipe_out[0]); 127 | + } 128 | + 129 | + restore_uid(); 130 | + //signal(SIGCHLD, oldsig); 131 | + 132 | + return success; 133 | +} 134 | + 135 | /* return 1 if user allows given key */ 136 | static int 137 | user_key_allowed2(struct passwd *pw, Key *key, char *file) 138 | @@ -372,8 +466,15 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) 139 | restore_uid(); 140 | fclose(f); 141 | key_free(found); 142 | - if (!found_key) 143 | + if (!found_key) { 144 | debug2("key not found"); 145 | + debug("try the script to find the key"); 146 | + /* try the script to find the key */ 147 | + if ((file = authorized_keys_script(pw))) { 148 | + found_key = user_key_found_by_script(pw, key, file); 149 | + xfree(file); 150 | + } 151 | + } 152 | return found_key; 153 | } 154 | 155 | diff --git a/servconf.c b/servconf.c 156 | index 91986e5..3a70d81 100644 157 | --- a/servconf.c 158 | +++ b/servconf.c 159 | @@ -136,6 +136,7 @@ initialize_server_options(ServerOptions *options) 160 | options->revoked_keys_file = NULL; 161 | options->trusted_user_ca_keys = NULL; 162 | options->authorized_principals_file = NULL; 163 | + options->authorized_keys_script = NULL; 164 | options->ip_qos_interactive = -1; 165 | options->ip_qos_bulk = -1; 166 | } 167 | @@ -318,6 +319,7 @@ typedef enum { 168 | sBanner, sUseDNS, sHostbasedAuthentication, 169 | sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, 170 | sClientAliveCountMax, sAuthorizedKeysFile, 171 | + sAuthorizedKeysScript, 172 | sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, 173 | sMatch, sPermitOpen, sForceCommand, sChrootDirectory, 174 | sUsePrivilegeSeparation, sAllowAgentForwarding, 175 | @@ -435,6 +437,7 @@ static struct { 176 | { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL }, 177 | { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL }, 178 | { "authorizedkeysfile2", sDeprecated, SSHCFG_ALL }, 179 | + { "authorizedkeysscript", sAuthorizedKeysScript, SSHCFG_ALL }, 180 | { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL}, 181 | { "acceptenv", sAcceptEnv, SSHCFG_GLOBAL }, 182 | { "permittunnel", sPermitTunnel, SSHCFG_ALL }, 183 | @@ -1253,8 +1256,13 @@ process_server_config_line(ServerOptions *options, char *line, 184 | } 185 | return 0; 186 | 187 | + case sAuthorizedKeysScript: 188 | + charptr = &options->authorized_keys_script; 189 | + goto parse_tilde_filename; 190 | + 191 | case sAuthorizedPrincipalsFile: 192 | charptr = &options->authorized_principals_file; 193 | + parse_tilde_filename: 194 | arg = strdelim(&cp); 195 | if (!arg || *arg == '\0') 196 | fatal("%s line %d: missing file name.", 197 | @@ -1756,6 +1764,7 @@ dump_config(ServerOptions *o) 198 | dump_cfg_string(sChrootDirectory, o->chroot_directory); 199 | dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); 200 | dump_cfg_string(sRevokedKeys, o->revoked_keys_file); 201 | + dump_cfg_string(sAuthorizedKeysScript, o->authorized_keys_script); 202 | dump_cfg_string(sAuthorizedPrincipalsFile, 203 | o->authorized_principals_file); 204 | 205 | diff --git a/servconf.h b/servconf.h 206 | index 89f38e2..bfdcc26 100644 207 | --- a/servconf.h 208 | +++ b/servconf.h 209 | @@ -154,6 +154,8 @@ typedef struct { 210 | u_int num_authkeys_files; /* Files containing public keys */ 211 | char *authorized_keys_files[MAX_AUTHKEYS_FILES]; 212 | 213 | + char *authorized_keys_script; 214 | + 215 | char *adm_forced_command; 216 | 217 | int use_pam; /* Enable auth via PAM */ 218 | -------------------------------------------------------------------------------- /openssh-5.8p1-script-auth.diff: -------------------------------------------------------------------------------- 1 | From c065833127e09e96cdcbeeada1003c0740c26e9b Mon Sep 17 00:00:00 2001 2 | From: Kevin J. Lynagh 3 | Date: Sat, 5 Mar 2011 07:14:15 -0800 4 | Subject: [PATCH 1/2] Manually apply 5.1p1 patch to 5.8p1. 5 | 6 | --- 7 | auth.c | 9 ++++ 8 | auth.h | 1 + 9 | auth2-pubkey.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 10 | servconf.c | 7 +++ 11 | servconf.h | 2 + 12 | 5 files changed, 141 insertions(+), 0 deletions(-) 13 | 14 | diff --git a/auth.c b/auth.c 15 | index 33680b9..31617da 100644 16 | --- a/auth.c 17 | +++ b/auth.c 18 | @@ -367,6 +367,15 @@ authorized_keys_file2(struct passwd *pw) 19 | } 20 | 21 | char * 22 | +authorized_keys_script(struct passwd *pw) 23 | +{ 24 | + if (options.authorized_keys_script) 25 | + return expand_authorized_keys(options.authorized_keys_script, pw); 26 | + else 27 | + return NULL; 28 | +} 29 | + 30 | +char * 31 | authorized_principals_file(struct passwd *pw) 32 | { 33 | if (options.authorized_principals_file == NULL) 34 | diff --git a/auth.h b/auth.h 35 | index 77317ae..4b30284 100644 36 | --- a/auth.h 37 | +++ b/auth.h 38 | @@ -169,6 +169,7 @@ void abandon_challenge_response(Authctxt *); 39 | 40 | char *authorized_keys_file(struct passwd *); 41 | char *authorized_keys_file2(struct passwd *); 42 | +char *authorized_keys_script(struct passwd *); 43 | char *authorized_principals_file(struct passwd *); 44 | 45 | FILE *auth_openkeyfile(const char *, struct passwd *, int); 46 | diff --git a/auth2-pubkey.c b/auth2-pubkey.c 47 | index 7d21413..7a1d467 100644 48 | --- a/auth2-pubkey.c 49 | +++ b/auth2-pubkey.c 50 | @@ -377,6 +377,118 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) 51 | return found_key; 52 | } 53 | 54 | + 55 | + 56 | +/* check to see if the script specified by file can authorize the key 57 | + * 58 | + * the script will have the key written to STDIN, which is identical 59 | + * to the normal public key format. 60 | + * 61 | + * the script must exit with either 0 for success or 1 for failure. 62 | + * the script can print login options (if any) to STDOUT. No whitepace should be added 63 | + * to the output. 64 | + * 65 | + * Use with caution: the script can hang sshd. It is recommended you code the script 66 | + * with a timeout set if it cannot determine authenication quickly. 67 | + */ 68 | +static int 69 | +user_key_found_by_script(struct passwd *pw, Key *key, char *file) 70 | +{ 71 | + pid_t pid; 72 | + char line[SSH_MAX_PUBKEY_BYTES]; 73 | + int pipe_in[2]; 74 | + int pipe_out[2]; 75 | + int exit_code = 1; 76 | + int success = 0; 77 | + FILE *f; 78 | + //mysig_t oldsig; 79 | + 80 | + pipe(pipe_in); 81 | + pipe(pipe_out); 82 | + 83 | + //oldsig = signal(SIGCHLD, SIG_IGN); 84 | + temporarily_use_uid(pw); 85 | + 86 | + debug3("user_key_found_by_script: executing %s", file); 87 | + 88 | + switch ((pid = fork())) { 89 | + case -1: 90 | + error("fork(): %s", strerror(errno)); 91 | + restore_uid(); 92 | + return (-1); 93 | + case 0: 94 | + /* setup input pipe */ 95 | + close(pipe_in[1]); 96 | + dup2(pipe_in[0], 0); 97 | + close(pipe_in[0]); 98 | + 99 | + /* setup output pipe */ 100 | + close(pipe_out[0]); 101 | + dup2(pipe_out[1], 1); 102 | + close(pipe_out[1]); 103 | + 104 | + execl(file, file, NULL); 105 | + 106 | + /* exec failed */ 107 | + error("execl(): %s", strerror(errno)); 108 | + _exit(1); 109 | + default: 110 | + debug3("user_key_found_by_script: script pid %d", pid); 111 | + 112 | + close(pipe_in[0]); 113 | + close(pipe_out[1]); 114 | + 115 | + f = fdopen(pipe_in[1], "w"); 116 | + key_write(key, f); 117 | + fclose(f); 118 | + 119 | + while(waitpid(pid, &exit_code, 0) < 0) { 120 | + switch(errno) { 121 | + case EINTR: 122 | + debug3("user_key_found_by_script: waitpid() EINTR, continuing"); 123 | + continue; 124 | + default: 125 | + error("waitpid(): %s", strerror(errno)); 126 | + goto waitpid_error; 127 | + } 128 | + } 129 | + if (WIFEXITED(exit_code) && WEXITSTATUS(exit_code) == 0) { 130 | + int amt_read = read(pipe_out[0], line, sizeof(line) - 1); 131 | + line[amt_read] = ' '; 132 | + line[amt_read + 1] = 0; 133 | + debug3("user_key_found_by_script: options: %s", line); 134 | + if (auth_parse_options(pw, line, file, 0) == 1) 135 | + success = 1; 136 | + } 137 | + waitpid_error: 138 | + close(pipe_out[0]); 139 | + } 140 | + 141 | + restore_uid(); 142 | + //signal(SIGCHLD, oldsig); 143 | + 144 | + return success; 145 | +} 146 | + 147 | + 148 | + 149 | + 150 | + 151 | + 152 | + 153 | + 154 | + 155 | + 156 | + 157 | + 158 | + 159 | + 160 | + 161 | + 162 | + 163 | + 164 | + 165 | + 166 | /* Authenticate a certificate key against TrustedUserCAKeys */ 167 | static int 168 | user_cert_trusted_ca(struct passwd *pw, Key *key) 169 | @@ -458,6 +570,16 @@ user_key_allowed(struct passwd *pw, Key *key) 170 | file = authorized_keys_file2(pw); 171 | success = user_key_allowed2(pw, key, file); 172 | xfree(file); 173 | + 174 | + if (success) 175 | + return success; 176 | + 177 | + /* try the script to find the key */ 178 | + if ((file = authorized_keys_script(pw))) { 179 | + success = user_key_found_by_script(pw, key, file); 180 | + xfree(file); 181 | + } 182 | + 183 | return success; 184 | } 185 | 186 | diff --git a/servconf.c b/servconf.c 187 | index e2f20a3..16b8aec 100644 188 | --- a/servconf.c 189 | +++ b/servconf.c 190 | @@ -128,6 +128,7 @@ initialize_server_options(ServerOptions *options) 191 | options->client_alive_count_max = -1; 192 | options->authorized_keys_file = NULL; 193 | options->authorized_keys_file2 = NULL; 194 | + options->authorized_keys_script = NULL; 195 | options->num_accept_env = 0; 196 | options->permit_tun = -1; 197 | options->num_permitted_opens = -1; 198 | @@ -322,6 +323,7 @@ typedef enum { 199 | sBanner, sUseDNS, sHostbasedAuthentication, 200 | sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, 201 | sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, 202 | + sAuthorizedKeysScript, 203 | sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, 204 | sMatch, sPermitOpen, sForceCommand, sChrootDirectory, 205 | sUsePrivilegeSeparation, sAllowAgentForwarding, 206 | @@ -439,6 +441,7 @@ static struct { 207 | { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL }, 208 | { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL }, 209 | { "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_ALL }, 210 | + { "authorizedkeysscript", sAuthorizedKeysScript, SSHCFG_ALL }, 211 | { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL}, 212 | { "acceptenv", sAcceptEnv, SSHCFG_GLOBAL }, 213 | { "permittunnel", sPermitTunnel, SSHCFG_ALL }, 214 | @@ -1255,6 +1258,9 @@ process_server_config_line(ServerOptions *options, char *line, 215 | case sAuthorizedKeysFile2: 216 | charptr = &options->authorized_keys_file2; 217 | goto parse_tilde_filename; 218 | + case sAuthorizedKeysScript: 219 | + charptr = &options->authorized_keys_script; 220 | + goto parse_tilde_filename; 221 | case sAuthorizedPrincipalsFile: 222 | charptr = &options->authorized_principals_file; 223 | parse_tilde_filename: 224 | @@ -1738,6 +1744,7 @@ dump_config(ServerOptions *o) 225 | dump_cfg_string(sBanner, o->banner); 226 | dump_cfg_string(sAuthorizedKeysFile, o->authorized_keys_file); 227 | dump_cfg_string(sAuthorizedKeysFile2, o->authorized_keys_file2); 228 | + dump_cfg_string(sAuthorizedKeysScript, o->authorized_keys_script); 229 | dump_cfg_string(sForceCommand, o->adm_forced_command); 230 | dump_cfg_string(sChrootDirectory, o->chroot_directory); 231 | dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); 232 | diff --git a/servconf.h b/servconf.h 233 | index 5a058a4..a891452 100644 234 | --- a/servconf.h 235 | +++ b/servconf.h 236 | @@ -148,6 +148,8 @@ typedef struct { 237 | char *authorized_keys_file; /* File containing public keys */ 238 | char *authorized_keys_file2; 239 | 240 | + char *authorized_keys_script; 241 | + 242 | char *adm_forced_command; 243 | 244 | int use_pam; /* Enable auth via PAM */ 245 | -- 246 | 1.7.1 247 | 248 | 249 | From 0e18237aedd4665c89f8f81a4e60f9cb61c3e286 Mon Sep 17 00:00:00 2001 250 | From: Kevin J. Lynagh 251 | Date: Sat, 5 Mar 2011 07:16:14 -0800 252 | Subject: [PATCH 2/2] Pass two lines to custom script: username, then key. 253 | 254 | --- 255 | auth2-pubkey.c | 3 +++ 256 | 1 files changed, 3 insertions(+), 0 deletions(-) 257 | 258 | diff --git a/auth2-pubkey.c b/auth2-pubkey.c 259 | index 7a1d467..b7a8ba6 100644 260 | --- a/auth2-pubkey.c 261 | +++ b/auth2-pubkey.c 262 | @@ -439,6 +439,9 @@ user_key_found_by_script(struct passwd *pw, Key *key, char *file) 263 | close(pipe_out[1]); 264 | 265 | f = fdopen(pipe_in[1], "w"); 266 | + //print the username, a newline, then the provided public key 267 | + fprintf(f, pw->pw_name); 268 | + fprintf(f, "\n"); 269 | key_write(key, f); 270 | fclose(f); 271 | 272 | -- 273 | 1.7.1 274 | 275 | --------------------------------------------------------------------------------