1 |
/* |
2 |
* Copyright (c) 2002 Markus Friedl. 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 |
6 |
* are met: |
7 |
* 1. Redistributions of source code must retain the above copyright |
8 |
* notice, this list of conditions and the following disclaimer. |
9 |
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
* notice, this list of conditions and the following disclaimer in the |
11 |
* documentation and/or other materials provided with the distribution. |
12 |
* |
13 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
14 |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
15 |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
16 |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
17 |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
18 |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
19 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
20 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
21 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
22 |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
23 |
*/ |
24 |
#include "includes.h" |
25 |
RCSID("$OpenBSD: ssh-keysign.c,v 1.19 2005/09/13 23:40:07 djm Exp $"); |
26 |
|
27 |
#include <openssl/evp.h> |
28 |
#include <openssl/rand.h> |
29 |
#include <openssl/rsa.h> |
30 |
|
31 |
#include "log.h" |
32 |
#include "key.h" |
33 |
#include "ssh.h" |
34 |
#include "ssh2.h" |
35 |
#include "misc.h" |
36 |
#include "xmalloc.h" |
37 |
#include "buffer.h" |
38 |
#include "bufaux.h" |
39 |
#include "authfile.h" |
40 |
#include "msg.h" |
41 |
#include "canohost.h" |
42 |
#include "pathnames.h" |
43 |
#include "readconf.h" |
44 |
#include "uidswap.h" |
45 |
|
46 |
/* XXX readconf.c needs these */ |
47 |
uid_t original_real_uid; |
48 |
|
49 |
extern char *__progname; |
50 |
|
51 |
static int |
52 |
valid_request(struct passwd *pw, char *host, Key **ret, u_char *data, |
53 |
u_int datalen) |
54 |
{ |
55 |
Buffer b; |
56 |
Key *key = NULL; |
57 |
u_char *pkblob; |
58 |
u_int blen, len; |
59 |
char *pkalg, *p; |
60 |
int pktype, fail; |
61 |
|
62 |
fail = 0; |
63 |
|
64 |
buffer_init(&b); |
65 |
buffer_append(&b, data, datalen); |
66 |
|
67 |
/* session id, currently limited to SHA1 (20 bytes) */ |
68 |
p = buffer_get_string(&b, &len); |
69 |
if (len != 20) |
70 |
fail++; |
71 |
xfree(p); |
72 |
|
73 |
if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST) |
74 |
fail++; |
75 |
|
76 |
/* server user */ |
77 |
buffer_skip_string(&b); |
78 |
|
79 |
/* service */ |
80 |
p = buffer_get_string(&b, NULL); |
81 |
if (strcmp("ssh-connection", p) != 0) |
82 |
fail++; |
83 |
xfree(p); |
84 |
|
85 |
/* method */ |
86 |
p = buffer_get_string(&b, NULL); |
87 |
if (strcmp("hostbased", p) != 0) |
88 |
fail++; |
89 |
xfree(p); |
90 |
|
91 |
/* pubkey */ |
92 |
pkalg = buffer_get_string(&b, NULL); |
93 |
pkblob = buffer_get_string(&b, &blen); |
94 |
|
95 |
pktype = key_type_from_name(pkalg); |
96 |
if (pktype == KEY_UNSPEC) |
97 |
fail++; |
98 |
else if ((key = key_from_blob(pkblob, blen)) == NULL) |
99 |
fail++; |
100 |
else if (key->type != pktype) |
101 |
fail++; |
102 |
xfree(pkalg); |
103 |
xfree(pkblob); |
104 |
|
105 |
/* client host name, handle trailing dot */ |
106 |
p = buffer_get_string(&b, &len); |
107 |
debug2("valid_request: check expect chost %s got %s", host, p); |
108 |
if (strlen(host) != len - 1) |
109 |
fail++; |
110 |
else if (p[len - 1] != '.') |
111 |
fail++; |
112 |
else if (strncasecmp(host, p, len - 1) != 0) |
113 |
fail++; |
114 |
xfree(p); |
115 |
|
116 |
/* local user */ |
117 |
p = buffer_get_string(&b, NULL); |
118 |
|
119 |
if (strcmp(pw->pw_name, p) != 0) |
120 |
fail++; |
121 |
xfree(p); |
122 |
|
123 |
/* end of message */ |
124 |
if (buffer_len(&b) != 0) |
125 |
fail++; |
126 |
buffer_free(&b); |
127 |
|
128 |
debug3("valid_request: fail %d", fail); |
129 |
|
130 |
if (fail && key != NULL) |
131 |
key_free(key); |
132 |
else |
133 |
*ret = key; |
134 |
|
135 |
return (fail ? -1 : 0); |
136 |
} |
137 |
|
138 |
int |
139 |
main(int argc, char **argv) |
140 |
{ |
141 |
Buffer b; |
142 |
Options options; |
143 |
Key *keys[2], *key; |
144 |
struct passwd *pw; |
145 |
int key_fd[2], i, found, version = 2, fd; |
146 |
u_char *signature, *data; |
147 |
char *host; |
148 |
u_int slen, dlen; |
149 |
u_int32_t rnd[256]; |
150 |
|
151 |
/* Ensure that stdin and stdout are connected */ |
152 |
if ((fd = open(_PATH_DEVNULL, O_RDWR)) < 2) |
153 |
exit(1); |
154 |
/* Leave /dev/null fd iff it is attached to stderr */ |
155 |
if (fd > 2) |
156 |
close(fd); |
157 |
|
158 |
key_fd[0] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY); |
159 |
key_fd[1] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY); |
160 |
|
161 |
original_real_uid = getuid(); /* XXX readconf.c needs this */ |
162 |
if ((pw = getpwuid(original_real_uid)) == NULL) |
163 |
fatal("getpwuid failed"); |
164 |
pw = pwcopy(pw); |
165 |
|
166 |
permanently_set_uid(pw); |
167 |
|
168 |
init_rng(); |
169 |
seed_rng(); |
170 |
arc4random_stir(); |
171 |
|
172 |
#ifdef DEBUG_SSH_KEYSIGN |
173 |
log_init("ssh-keysign", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 0); |
174 |
#endif |
175 |
|
176 |
/* verify that ssh-keysign is enabled by the admin */ |
177 |
initialize_options(&options); |
178 |
(void)read_config_file(_PATH_HOST_CONFIG_FILE, "", &options, 0); |
179 |
fill_default_options(&options); |
180 |
if (options.enable_ssh_keysign != 1) |
181 |
fatal("ssh-keysign not enabled in %s", |
182 |
_PATH_HOST_CONFIG_FILE); |
183 |
|
184 |
if (key_fd[0] == -1 && key_fd[1] == -1) |
185 |
fatal("could not open any host key"); |
186 |
|
187 |
SSLeay_add_all_algorithms(); |
188 |
for (i = 0; i < 256; i++) |
189 |
rnd[i] = arc4random(); |
190 |
RAND_seed(rnd, sizeof(rnd)); |
191 |
|
192 |
found = 0; |
193 |
for (i = 0; i < 2; i++) { |
194 |
keys[i] = NULL; |
195 |
if (key_fd[i] == -1) |
196 |
continue; |
197 |
keys[i] = key_load_private_pem(key_fd[i], KEY_UNSPEC, |
198 |
NULL, NULL); |
199 |
close(key_fd[i]); |
200 |
if (keys[i] != NULL) |
201 |
found = 1; |
202 |
} |
203 |
if (!found) |
204 |
fatal("no hostkey found"); |
205 |
|
206 |
buffer_init(&b); |
207 |
if (ssh_msg_recv(STDIN_FILENO, &b) < 0) |
208 |
fatal("ssh_msg_recv failed"); |
209 |
if (buffer_get_char(&b) != version) |
210 |
fatal("bad version"); |
211 |
fd = buffer_get_int(&b); |
212 |
if ((fd == STDIN_FILENO) || (fd == STDOUT_FILENO)) |
213 |
fatal("bad fd"); |
214 |
if ((host = get_local_name(fd)) == NULL) |
215 |
fatal("cannot get sockname for fd"); |
216 |
|
217 |
data = buffer_get_string(&b, &dlen); |
218 |
if (valid_request(pw, host, &key, data, dlen) < 0) |
219 |
fatal("not a valid request"); |
220 |
xfree(host); |
221 |
|
222 |
found = 0; |
223 |
for (i = 0; i < 2; i++) { |
224 |
if (keys[i] != NULL && |
225 |
key_equal(key, keys[i])) { |
226 |
found = 1; |
227 |
break; |
228 |
} |
229 |
} |
230 |
if (!found) |
231 |
fatal("no matching hostkey found"); |
232 |
|
233 |
if (key_sign(keys[i], &signature, &slen, data, dlen) != 0) |
234 |
fatal("key_sign failed"); |
235 |
xfree(data); |
236 |
|
237 |
/* send reply */ |
238 |
buffer_clear(&b); |
239 |
buffer_put_string(&b, signature, slen); |
240 |
if (ssh_msg_send(STDOUT_FILENO, version, &b) == -1) |
241 |
fatal("ssh_msg_send failed"); |
242 |
|
243 |
return (0); |
244 |
} |