├── 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 |
--------------------------------------------------------------------------------