diff options
Diffstat (limited to 'auth-rsa.c')
-rw-r--r-- | auth-rsa.c | 805 |
1 files changed, 385 insertions, 420 deletions
diff --git a/auth-rsa.c b/auth-rsa.c index f2295078b..88841482b 100644 --- a/auth-rsa.c +++ b/auth-rsa.c | |||
@@ -1,22 +1,22 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | auth-rsa.c | 3 | * auth-rsa.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Mon Mar 27 01:46:52 1995 ylo | 10 | * Created: Mon Mar 27 01:46:52 1995 ylo |
11 | 11 | * | |
12 | RSA-based authentication. This code determines whether to admit a login | 12 | * RSA-based authentication. This code determines whether to admit a login |
13 | based on RSA authentication. This file also contains functions to check | 13 | * based on RSA authentication. This file also contains functions to check |
14 | validity of the host key. | 14 | * validity of the host key. |
15 | 15 | * | |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include "includes.h" | 18 | #include "includes.h" |
19 | RCSID("$Id: auth-rsa.c,v 1.8 1999/11/18 21:25:48 damien Exp $"); | 19 | RCSID("$Id: auth-rsa.c,v 1.9 1999/11/24 13:26:21 damien Exp $"); |
20 | 20 | ||
21 | #include "rsa.h" | 21 | #include "rsa.h" |
22 | #include "packet.h" | 22 | #include "packet.h" |
@@ -50,7 +50,7 @@ extern unsigned char session_id[16]; | |||
50 | /* The .ssh/authorized_keys file contains public keys, one per line, in the | 50 | /* The .ssh/authorized_keys file contains public keys, one per line, in the |
51 | following format: | 51 | following format: |
52 | options bits e n comment | 52 | options bits e n comment |
53 | where bits, e and n are decimal numbers, | 53 | where bits, e and n are decimal numbers, |
54 | and comment is any string of characters up to newline. The maximum | 54 | and comment is any string of characters up to newline. The maximum |
55 | length of a line is 8000 characters. See the documentation for a | 55 | length of a line is 8000 characters. See the documentation for a |
56 | description of the options. | 56 | description of the options. |
@@ -63,71 +63,69 @@ extern unsigned char session_id[16]; | |||
63 | int | 63 | int |
64 | auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n) | 64 | auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n) |
65 | { | 65 | { |
66 | BIGNUM *challenge, *encrypted_challenge, *aux; | 66 | BIGNUM *challenge, *encrypted_challenge, *aux; |
67 | RSA *pk; | 67 | RSA *pk; |
68 | BN_CTX *ctx = BN_CTX_new(); | 68 | BN_CTX *ctx = BN_CTX_new(); |
69 | unsigned char buf[32], mdbuf[16], response[16]; | 69 | unsigned char buf[32], mdbuf[16], response[16]; |
70 | MD5_CTX md; | 70 | MD5_CTX md; |
71 | unsigned int i; | 71 | unsigned int i; |
72 | int plen, len; | 72 | int plen, len; |
73 | 73 | ||
74 | encrypted_challenge = BN_new(); | 74 | encrypted_challenge = BN_new(); |
75 | challenge = BN_new(); | 75 | challenge = BN_new(); |
76 | aux = BN_new(); | 76 | aux = BN_new(); |
77 | 77 | ||
78 | /* Generate a random challenge. */ | 78 | /* Generate a random challenge. */ |
79 | BN_rand(challenge, 256, 0, 0); | 79 | BN_rand(challenge, 256, 0, 0); |
80 | BN_mod(challenge, challenge, n, ctx); | 80 | BN_mod(challenge, challenge, n, ctx); |
81 | 81 | ||
82 | /* Create the public key data structure. */ | 82 | /* Create the public key data structure. */ |
83 | pk = RSA_new(); | 83 | pk = RSA_new(); |
84 | pk->e = BN_new(); | 84 | pk->e = BN_new(); |
85 | BN_copy(pk->e, e); | 85 | BN_copy(pk->e, e); |
86 | pk->n = BN_new(); | 86 | pk->n = BN_new(); |
87 | BN_copy(pk->n, n); | 87 | BN_copy(pk->n, n); |
88 | 88 | ||
89 | /* Encrypt the challenge with the public key. */ | 89 | /* Encrypt the challenge with the public key. */ |
90 | rsa_public_encrypt(encrypted_challenge, challenge, pk); | 90 | rsa_public_encrypt(encrypted_challenge, challenge, pk); |
91 | RSA_free(pk); | 91 | RSA_free(pk); |
92 | 92 | ||
93 | /* Send the encrypted challenge to the client. */ | 93 | /* Send the encrypted challenge to the client. */ |
94 | packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); | 94 | packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); |
95 | packet_put_bignum(encrypted_challenge); | 95 | packet_put_bignum(encrypted_challenge); |
96 | packet_send(); | 96 | packet_send(); |
97 | packet_write_wait(); | 97 | packet_write_wait(); |
98 | 98 | ||
99 | /* The response is MD5 of decrypted challenge plus session id. */ | 99 | /* The response is MD5 of decrypted challenge plus session id. */ |
100 | len = BN_num_bytes(challenge); | 100 | len = BN_num_bytes(challenge); |
101 | if (len <= 0 || len > 32) | 101 | if (len <= 0 || len > 32) |
102 | fatal("auth_rsa_challenge_dialog: bad challenge length %d", len); | 102 | fatal("auth_rsa_challenge_dialog: bad challenge length %d", len); |
103 | memset(buf, 0, 32); | 103 | memset(buf, 0, 32); |
104 | BN_bn2bin(challenge, buf + 32 - len); | 104 | BN_bn2bin(challenge, buf + 32 - len); |
105 | MD5_Init(&md); | 105 | MD5_Init(&md); |
106 | MD5_Update(&md, buf, 32); | 106 | MD5_Update(&md, buf, 32); |
107 | MD5_Update(&md, session_id, 16); | 107 | MD5_Update(&md, session_id, 16); |
108 | MD5_Final(mdbuf, &md); | 108 | MD5_Final(mdbuf, &md); |
109 | 109 | ||
110 | /* We will no longer need these. */ | 110 | /* We will no longer need these. */ |
111 | BN_clear_free(encrypted_challenge); | 111 | BN_clear_free(encrypted_challenge); |
112 | BN_clear_free(challenge); | 112 | BN_clear_free(challenge); |
113 | BN_clear_free(aux); | 113 | BN_clear_free(aux); |
114 | BN_CTX_free(ctx); | 114 | BN_CTX_free(ctx); |
115 | 115 | ||
116 | /* Wait for a response. */ | 116 | /* Wait for a response. */ |
117 | packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE); | 117 | packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE); |
118 | packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE); | 118 | packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE); |
119 | for (i = 0; i < 16; i++) | 119 | for (i = 0; i < 16; i++) |
120 | response[i] = packet_get_char(); | 120 | response[i] = packet_get_char(); |
121 | 121 | ||
122 | /* Verify that the response is the original challenge. */ | 122 | /* Verify that the response is the original challenge. */ |
123 | if (memcmp(response, mdbuf, 16) != 0) | 123 | if (memcmp(response, mdbuf, 16) != 0) { |
124 | { | 124 | /* Wrong answer. */ |
125 | /* Wrong answer. */ | 125 | return 0; |
126 | return 0; | 126 | } |
127 | } | 127 | /* Correct answer. */ |
128 | 128 | return 1; | |
129 | /* Correct answer. */ | ||
130 | return 1; | ||
131 | } | 129 | } |
132 | 130 | ||
133 | /* Performs the RSA authentication dialog with the client. This returns | 131 | /* Performs the RSA authentication dialog with the client. This returns |
@@ -137,357 +135,324 @@ auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n) | |||
137 | int | 135 | int |
138 | auth_rsa(struct passwd *pw, BIGNUM *client_n) | 136 | auth_rsa(struct passwd *pw, BIGNUM *client_n) |
139 | { | 137 | { |
140 | extern ServerOptions options; | 138 | extern ServerOptions options; |
141 | char line[8192], file[1024]; | 139 | char line[8192], file[1024]; |
142 | int authenticated; | 140 | int authenticated; |
143 | unsigned int bits; | 141 | unsigned int bits; |
144 | FILE *f; | 142 | FILE *f; |
145 | unsigned long linenum = 0; | 143 | unsigned long linenum = 0; |
146 | struct stat st; | 144 | struct stat st; |
147 | BIGNUM *e, *n; | 145 | BIGNUM *e, *n; |
148 | 146 | ||
149 | /* Temporarily use the user's uid. */ | 147 | /* Temporarily use the user's uid. */ |
150 | temporarily_use_uid(pw->pw_uid); | 148 | temporarily_use_uid(pw->pw_uid); |
151 | 149 | ||
152 | /* The authorized keys. */ | 150 | /* The authorized keys. */ |
153 | snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, | 151 | snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, |
154 | SSH_USER_PERMITTED_KEYS); | 152 | SSH_USER_PERMITTED_KEYS); |
155 | 153 | ||
156 | /* Fail quietly if file does not exist */ | 154 | /* Fail quietly if file does not exist */ |
157 | if (stat(file, &st) < 0) | 155 | if (stat(file, &st) < 0) { |
158 | { | 156 | /* Restore the privileged uid. */ |
159 | /* Restore the privileged uid. */ | 157 | restore_uid(); |
160 | restore_uid(); | 158 | return 0; |
161 | return 0; | ||
162 | } | ||
163 | |||
164 | /* Open the file containing the authorized keys. */ | ||
165 | f = fopen(file, "r"); | ||
166 | if (!f) | ||
167 | { | ||
168 | /* Restore the privileged uid. */ | ||
169 | restore_uid(); | ||
170 | packet_send_debug("Could not open %.900s for reading.", file); | ||
171 | packet_send_debug("If your home is on an NFS volume, it may need to be world-readable."); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | if (options.strict_modes) { | ||
176 | int fail=0; | ||
177 | char buf[1024]; | ||
178 | /* Check open file in order to avoid open/stat races */ | ||
179 | if (fstat(fileno(f), &st) < 0 || | ||
180 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
181 | (st.st_mode & 022) != 0) { | ||
182 | snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " | ||
183 | "bad ownership or modes for '%s'.", pw->pw_name, file); | ||
184 | fail=1; | ||
185 | }else{ | ||
186 | /* Check path to SSH_USER_PERMITTED_KEYS */ | ||
187 | int i; | ||
188 | static const char *check[] = { | ||
189 | "", SSH_USER_DIR, NULL | ||
190 | }; | ||
191 | for (i=0; check[i]; i++) { | ||
192 | snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]); | ||
193 | if (stat(line, &st) < 0 || | ||
194 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
195 | (st.st_mode & 022) != 0) { | ||
196 | snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " | ||
197 | "bad ownership or modes for '%s'.", pw->pw_name, line); | ||
198 | fail=1; | ||
199 | break; | ||
200 | } | ||
201 | } | ||
202 | } | ||
203 | if (fail) { | ||
204 | log(buf); | ||
205 | packet_send_debug(buf); | ||
206 | restore_uid(); | ||
207 | return 0; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | /* Flag indicating whether authentication has succeeded. */ | ||
212 | authenticated = 0; | ||
213 | |||
214 | /* Initialize mp-int variables. */ | ||
215 | e = BN_new(); | ||
216 | n = BN_new(); | ||
217 | |||
218 | /* Go though the accepted keys, looking for the current key. If found, | ||
219 | perform a challenge-response dialog to verify that the user really has | ||
220 | the corresponding private key. */ | ||
221 | while (fgets(line, sizeof(line), f)) | ||
222 | { | ||
223 | char *cp; | ||
224 | char *options; | ||
225 | |||
226 | linenum++; | ||
227 | |||
228 | /* Skip leading whitespace. */ | ||
229 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | ||
230 | ; | ||
231 | |||
232 | /* Skip empty and comment lines. */ | ||
233 | if (!*cp || *cp == '\n' || *cp == '#') | ||
234 | continue; | ||
235 | |||
236 | /* Check if there are options for this key, and if so, save their | ||
237 | starting address and skip the option part for now. If there are no | ||
238 | options, set the starting address to NULL. */ | ||
239 | if (*cp < '0' || *cp > '9') | ||
240 | { | ||
241 | int quoted = 0; | ||
242 | options = cp; | ||
243 | for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) | ||
244 | { | ||
245 | if (*cp == '\\' && cp[1] == '"') | ||
246 | cp++; /* Skip both */ | ||
247 | else | ||
248 | if (*cp == '"') | ||
249 | quoted = !quoted; | ||
250 | } | ||
251 | } | 159 | } |
252 | else | 160 | /* Open the file containing the authorized keys. */ |
253 | options = NULL; | 161 | f = fopen(file, "r"); |
254 | 162 | if (!f) { | |
255 | /* Parse the key from the line. */ | 163 | /* Restore the privileged uid. */ |
256 | if (!auth_rsa_read_key(&cp, &bits, e, n)) | 164 | restore_uid(); |
257 | { | 165 | packet_send_debug("Could not open %.900s for reading.", file); |
258 | debug("%.100s, line %lu: bad key syntax", | 166 | packet_send_debug("If your home is on an NFS volume, it may need to be world-readable."); |
259 | SSH_USER_PERMITTED_KEYS, linenum); | 167 | return 0; |
260 | packet_send_debug("%.100s, line %lu: bad key syntax", | ||
261 | SSH_USER_PERMITTED_KEYS, linenum); | ||
262 | continue; | ||
263 | } | 168 | } |
264 | /* cp now points to the comment part. */ | 169 | if (options.strict_modes) { |
265 | 170 | int fail = 0; | |
266 | /* check the real bits */ | 171 | char buf[1024]; |
267 | if (bits != BN_num_bits(n)) | 172 | /* Check open file in order to avoid open/stat races */ |
268 | error("Warning: error in %s, line %ld: keysize mismatch: " | 173 | if (fstat(fileno(f), &st) < 0 || |
269 | "actual size %d vs. announced %d.", | 174 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || |
270 | file, linenum, BN_num_bits(n), bits); | 175 | (st.st_mode & 022) != 0) { |
271 | 176 | snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " | |
272 | /* Check if the we have found the desired key (identified by its | 177 | "bad ownership or modes for '%s'.", pw->pw_name, file); |
273 | modulus). */ | 178 | fail = 1; |
274 | if (BN_cmp(n, client_n) != 0) | 179 | } else { |
275 | continue; /* Wrong key. */ | 180 | /* Check path to SSH_USER_PERMITTED_KEYS */ |
276 | 181 | int i; | |
277 | /* We have found the desired key. */ | 182 | static const char *check[] = { |
278 | 183 | "", SSH_USER_DIR, NULL | |
279 | /* Perform the challenge-response dialog for this key. */ | 184 | }; |
280 | if (!auth_rsa_challenge_dialog(e, n)) | 185 | for (i = 0; check[i]; i++) { |
281 | { | 186 | snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]); |
282 | /* Wrong response. */ | 187 | if (stat(line, &st) < 0 || |
283 | log("Wrong response to RSA authentication challenge."); | 188 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || |
284 | packet_send_debug("Wrong response to RSA authentication challenge."); | 189 | (st.st_mode & 022) != 0) { |
285 | continue; | 190 | snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " |
286 | } | 191 | "bad ownership or modes for '%s'.", pw->pw_name, line); |
287 | 192 | fail = 1; | |
288 | /* Correct response. The client has been successfully authenticated. | 193 | break; |
289 | Note that we have not yet processed the options; this will be reset | 194 | } |
290 | if the options cause the authentication to be rejected. */ | 195 | } |
291 | authenticated = 1; | ||
292 | |||
293 | /* RSA part of authentication was accepted. Now process the options. */ | ||
294 | if (options) | ||
295 | { | ||
296 | while (*options && *options != ' ' && *options != '\t') | ||
297 | { | ||
298 | cp = "no-port-forwarding"; | ||
299 | if (strncmp(options, cp, strlen(cp)) == 0) | ||
300 | { | ||
301 | packet_send_debug("Port forwarding disabled."); | ||
302 | no_port_forwarding_flag = 1; | ||
303 | options += strlen(cp); | ||
304 | goto next_option; | ||
305 | } | ||
306 | cp = "no-agent-forwarding"; | ||
307 | if (strncmp(options, cp, strlen(cp)) == 0) | ||
308 | { | ||
309 | packet_send_debug("Agent forwarding disabled."); | ||
310 | no_agent_forwarding_flag = 1; | ||
311 | options += strlen(cp); | ||
312 | goto next_option; | ||
313 | } | ||
314 | cp = "no-X11-forwarding"; | ||
315 | if (strncmp(options, cp, strlen(cp)) == 0) | ||
316 | { | ||
317 | packet_send_debug("X11 forwarding disabled."); | ||
318 | no_x11_forwarding_flag = 1; | ||
319 | options += strlen(cp); | ||
320 | goto next_option; | ||
321 | } | 196 | } |
322 | cp = "no-pty"; | 197 | if (fail) { |
323 | if (strncmp(options, cp, strlen(cp)) == 0) | 198 | log(buf); |
324 | { | 199 | packet_send_debug(buf); |
325 | packet_send_debug("Pty allocation disabled."); | 200 | restore_uid(); |
326 | no_pty_flag = 1; | 201 | return 0; |
327 | options += strlen(cp); | ||
328 | goto next_option; | ||
329 | } | 202 | } |
330 | cp = "command=\""; | 203 | } |
331 | if (strncmp(options, cp, strlen(cp)) == 0) | 204 | /* Flag indicating whether authentication has succeeded. */ |
332 | { | 205 | authenticated = 0; |
333 | int i; | 206 | |
334 | options += strlen(cp); | 207 | /* Initialize mp-int variables. */ |
335 | forced_command = xmalloc(strlen(options) + 1); | 208 | e = BN_new(); |
336 | i = 0; | 209 | n = BN_new(); |
337 | while (*options) | 210 | |
338 | { | 211 | /* Go though the accepted keys, looking for the current key. If |
339 | if (*options == '"') | 212 | found, perform a challenge-response dialog to verify that the |
340 | break; | 213 | user really has the corresponding private key. */ |
341 | if (*options == '\\' && options[1] == '"') | 214 | while (fgets(line, sizeof(line), f)) { |
342 | { | 215 | char *cp; |
343 | options += 2; | 216 | char *options; |
344 | forced_command[i++] = '"'; | 217 | |
345 | continue; | 218 | linenum++; |
219 | |||
220 | /* Skip leading whitespace. */ | ||
221 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++); | ||
222 | |||
223 | /* Skip empty and comment lines. */ | ||
224 | if (!*cp || *cp == '\n' || *cp == '#') | ||
225 | continue; | ||
226 | |||
227 | /* Check if there are options for this key, and if so, | ||
228 | save their starting address and skip the option part | ||
229 | for now. If there are no options, set the starting | ||
230 | address to NULL. */ | ||
231 | if (*cp < '0' || *cp > '9') { | ||
232 | int quoted = 0; | ||
233 | options = cp; | ||
234 | for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { | ||
235 | if (*cp == '\\' && cp[1] == '"') | ||
236 | cp++; /* Skip both */ | ||
237 | else if (*cp == '"') | ||
238 | quoted = !quoted; | ||
346 | } | 239 | } |
347 | forced_command[i++] = *options++; | 240 | } else |
348 | } | 241 | options = NULL; |
349 | if (!*options) | 242 | |
350 | { | 243 | /* Parse the key from the line. */ |
351 | debug("%.100s, line %lu: missing end quote", | 244 | if (!auth_rsa_read_key(&cp, &bits, e, n)) { |
352 | SSH_USER_PERMITTED_KEYS, linenum); | 245 | debug("%.100s, line %lu: bad key syntax", |
353 | packet_send_debug("%.100s, line %lu: missing end quote", | 246 | SSH_USER_PERMITTED_KEYS, linenum); |
354 | SSH_USER_PERMITTED_KEYS, linenum); | 247 | packet_send_debug("%.100s, line %lu: bad key syntax", |
355 | continue; | 248 | SSH_USER_PERMITTED_KEYS, linenum); |
356 | } | 249 | continue; |
357 | forced_command[i] = 0; | ||
358 | packet_send_debug("Forced command: %.900s", forced_command); | ||
359 | options++; | ||
360 | goto next_option; | ||
361 | } | 250 | } |
362 | cp = "environment=\""; | 251 | /* cp now points to the comment part. */ |
363 | if (strncmp(options, cp, strlen(cp)) == 0) | 252 | |
364 | { | 253 | /* check the real bits */ |
365 | int i; | 254 | if (bits != BN_num_bits(n)) |
366 | char *s; | 255 | error("Warning: error in %s, line %ld: keysize mismatch: " |
367 | struct envstring *new_envstring; | 256 | "actual size %d vs. announced %d.", |
368 | options += strlen(cp); | 257 | file, linenum, BN_num_bits(n), bits); |
369 | s = xmalloc(strlen(options) + 1); | 258 | |
370 | i = 0; | 259 | /* Check if the we have found the desired key (identified by its modulus). */ |
371 | while (*options) | 260 | if (BN_cmp(n, client_n) != 0) |
372 | { | 261 | continue; /* Wrong key. */ |
373 | if (*options == '"') | 262 | |
374 | break; | 263 | /* We have found the desired key. */ |
375 | if (*options == '\\' && options[1] == '"') | 264 | |
376 | { | 265 | /* Perform the challenge-response dialog for this key. */ |
377 | options += 2; | 266 | if (!auth_rsa_challenge_dialog(e, n)) { |
378 | s[i++] = '"'; | 267 | /* Wrong response. */ |
379 | continue; | 268 | verbose("Wrong response to RSA authentication challenge."); |
380 | } | 269 | packet_send_debug("Wrong response to RSA authentication challenge."); |
381 | s[i++] = *options++; | 270 | continue; |
382 | } | ||
383 | if (!*options) | ||
384 | { | ||
385 | debug("%.100s, line %lu: missing end quote", | ||
386 | SSH_USER_PERMITTED_KEYS, linenum); | ||
387 | packet_send_debug("%.100s, line %lu: missing end quote", | ||
388 | SSH_USER_PERMITTED_KEYS, linenum); | ||
389 | continue; | ||
390 | } | ||
391 | s[i] = 0; | ||
392 | packet_send_debug("Adding to environment: %.900s", s); | ||
393 | debug("Adding to environment: %.900s", s); | ||
394 | options++; | ||
395 | new_envstring = xmalloc(sizeof(struct envstring)); | ||
396 | new_envstring->s = s; | ||
397 | new_envstring->next = custom_environment; | ||
398 | custom_environment = new_envstring; | ||
399 | goto next_option; | ||
400 | } | 271 | } |
401 | cp = "from=\""; | 272 | /* Correct response. The client has been successfully |
402 | if (strncmp(options, cp, strlen(cp)) == 0) | 273 | authenticated. Note that we have not yet processed the |
403 | { | 274 | options; this will be reset if the options cause the |
404 | char *patterns = xmalloc(strlen(options) + 1); | 275 | authentication to be rejected. */ |
405 | int i; | 276 | authenticated = 1; |
406 | options += strlen(cp); | 277 | |
407 | i = 0; | 278 | /* RSA part of authentication was accepted. Now process the options. */ |
408 | while (*options) | 279 | if (options) { |
409 | { | 280 | while (*options && *options != ' ' && *options != '\t') { |
410 | if (*options == '"') | 281 | cp = "no-port-forwarding"; |
411 | break; | 282 | if (strncmp(options, cp, strlen(cp)) == 0) { |
412 | if (*options == '\\' && options[1] == '"') | 283 | packet_send_debug("Port forwarding disabled."); |
413 | { | 284 | no_port_forwarding_flag = 1; |
414 | options += 2; | 285 | options += strlen(cp); |
415 | patterns[i++] = '"'; | 286 | goto next_option; |
416 | continue; | 287 | } |
288 | cp = "no-agent-forwarding"; | ||
289 | if (strncmp(options, cp, strlen(cp)) == 0) { | ||
290 | packet_send_debug("Agent forwarding disabled."); | ||
291 | no_agent_forwarding_flag = 1; | ||
292 | options += strlen(cp); | ||
293 | goto next_option; | ||
294 | } | ||
295 | cp = "no-X11-forwarding"; | ||
296 | if (strncmp(options, cp, strlen(cp)) == 0) { | ||
297 | packet_send_debug("X11 forwarding disabled."); | ||
298 | no_x11_forwarding_flag = 1; | ||
299 | options += strlen(cp); | ||
300 | goto next_option; | ||
301 | } | ||
302 | cp = "no-pty"; | ||
303 | if (strncmp(options, cp, strlen(cp)) == 0) { | ||
304 | packet_send_debug("Pty allocation disabled."); | ||
305 | no_pty_flag = 1; | ||
306 | options += strlen(cp); | ||
307 | goto next_option; | ||
308 | } | ||
309 | cp = "command=\""; | ||
310 | if (strncmp(options, cp, strlen(cp)) == 0) { | ||
311 | int i; | ||
312 | options += strlen(cp); | ||
313 | forced_command = xmalloc(strlen(options) + 1); | ||
314 | i = 0; | ||
315 | while (*options) { | ||
316 | if (*options == '"') | ||
317 | break; | ||
318 | if (*options == '\\' && options[1] == '"') { | ||
319 | options += 2; | ||
320 | forced_command[i++] = '"'; | ||
321 | continue; | ||
322 | } | ||
323 | forced_command[i++] = *options++; | ||
324 | } | ||
325 | if (!*options) { | ||
326 | debug("%.100s, line %lu: missing end quote", | ||
327 | SSH_USER_PERMITTED_KEYS, linenum); | ||
328 | packet_send_debug("%.100s, line %lu: missing end quote", | ||
329 | SSH_USER_PERMITTED_KEYS, linenum); | ||
330 | continue; | ||
331 | } | ||
332 | forced_command[i] = 0; | ||
333 | packet_send_debug("Forced command: %.900s", forced_command); | ||
334 | options++; | ||
335 | goto next_option; | ||
336 | } | ||
337 | cp = "environment=\""; | ||
338 | if (strncmp(options, cp, strlen(cp)) == 0) { | ||
339 | int i; | ||
340 | char *s; | ||
341 | struct envstring *new_envstring; | ||
342 | options += strlen(cp); | ||
343 | s = xmalloc(strlen(options) + 1); | ||
344 | i = 0; | ||
345 | while (*options) { | ||
346 | if (*options == '"') | ||
347 | break; | ||
348 | if (*options == '\\' && options[1] == '"') { | ||
349 | options += 2; | ||
350 | s[i++] = '"'; | ||
351 | continue; | ||
352 | } | ||
353 | s[i++] = *options++; | ||
354 | } | ||
355 | if (!*options) { | ||
356 | debug("%.100s, line %lu: missing end quote", | ||
357 | SSH_USER_PERMITTED_KEYS, linenum); | ||
358 | packet_send_debug("%.100s, line %lu: missing end quote", | ||
359 | SSH_USER_PERMITTED_KEYS, linenum); | ||
360 | continue; | ||
361 | } | ||
362 | s[i] = 0; | ||
363 | packet_send_debug("Adding to environment: %.900s", s); | ||
364 | debug("Adding to environment: %.900s", s); | ||
365 | options++; | ||
366 | new_envstring = xmalloc(sizeof(struct envstring)); | ||
367 | new_envstring->s = s; | ||
368 | new_envstring->next = custom_environment; | ||
369 | custom_environment = new_envstring; | ||
370 | goto next_option; | ||
371 | } | ||
372 | cp = "from=\""; | ||
373 | if (strncmp(options, cp, strlen(cp)) == 0) { | ||
374 | char *patterns = xmalloc(strlen(options) + 1); | ||
375 | int i; | ||
376 | options += strlen(cp); | ||
377 | i = 0; | ||
378 | while (*options) { | ||
379 | if (*options == '"') | ||
380 | break; | ||
381 | if (*options == '\\' && options[1] == '"') { | ||
382 | options += 2; | ||
383 | patterns[i++] = '"'; | ||
384 | continue; | ||
385 | } | ||
386 | patterns[i++] = *options++; | ||
387 | } | ||
388 | if (!*options) { | ||
389 | debug("%.100s, line %lu: missing end quote", | ||
390 | SSH_USER_PERMITTED_KEYS, linenum); | ||
391 | packet_send_debug("%.100s, line %lu: missing end quote", | ||
392 | SSH_USER_PERMITTED_KEYS, linenum); | ||
393 | continue; | ||
394 | } | ||
395 | patterns[i] = 0; | ||
396 | options++; | ||
397 | if (!match_hostname(get_canonical_hostname(), patterns, | ||
398 | strlen(patterns)) && | ||
399 | !match_hostname(get_remote_ipaddr(), patterns, | ||
400 | strlen(patterns))) { | ||
401 | log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).", | ||
402 | pw->pw_name, get_canonical_hostname(), | ||
403 | get_remote_ipaddr()); | ||
404 | packet_send_debug("Your host '%.200s' is not permitted to use this key for login.", | ||
405 | get_canonical_hostname()); | ||
406 | xfree(patterns); | ||
407 | authenticated = 0; | ||
408 | break; | ||
409 | } | ||
410 | xfree(patterns); | ||
411 | /* Host name matches. */ | ||
412 | goto next_option; | ||
413 | } | ||
414 | bad_option: | ||
415 | /* Unknown option. */ | ||
416 | log("Bad options in %.100s file, line %lu: %.50s", | ||
417 | SSH_USER_PERMITTED_KEYS, linenum, options); | ||
418 | packet_send_debug("Bad options in %.100s file, line %lu: %.50s", | ||
419 | SSH_USER_PERMITTED_KEYS, linenum, options); | ||
420 | authenticated = 0; | ||
421 | break; | ||
422 | |||
423 | next_option: | ||
424 | /* Skip the comma, and move to the next option | ||
425 | (or break out if there are no more). */ | ||
426 | if (!*options) | ||
427 | fatal("Bugs in auth-rsa.c option processing."); | ||
428 | if (*options == ' ' || *options == '\t') | ||
429 | break; /* End of options. */ | ||
430 | if (*options != ',') | ||
431 | goto bad_option; | ||
432 | options++; | ||
433 | /* Process the next option. */ | ||
434 | continue; | ||
417 | } | 435 | } |
418 | patterns[i++] = *options++; | ||
419 | } | ||
420 | if (!*options) | ||
421 | { | ||
422 | debug("%.100s, line %lu: missing end quote", | ||
423 | SSH_USER_PERMITTED_KEYS, linenum); | ||
424 | packet_send_debug("%.100s, line %lu: missing end quote", | ||
425 | SSH_USER_PERMITTED_KEYS, linenum); | ||
426 | continue; | ||
427 | } | ||
428 | patterns[i] = 0; | ||
429 | options++; | ||
430 | if (!match_hostname(get_canonical_hostname(), patterns, | ||
431 | strlen(patterns)) && | ||
432 | !match_hostname(get_remote_ipaddr(), patterns, | ||
433 | strlen(patterns))) | ||
434 | { | ||
435 | log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).", | ||
436 | pw->pw_name, get_canonical_hostname(), | ||
437 | get_remote_ipaddr()); | ||
438 | packet_send_debug("Your host '%.200s' is not permitted to use this key for login.", | ||
439 | get_canonical_hostname()); | ||
440 | xfree(patterns); | ||
441 | authenticated = 0; | ||
442 | break; | ||
443 | } | ||
444 | xfree(patterns); | ||
445 | /* Host name matches. */ | ||
446 | goto next_option; | ||
447 | } | 436 | } |
448 | bad_option: | 437 | /* Break out of the loop if authentication was successful; |
449 | /* Unknown option. */ | 438 | otherwise continue searching. */ |
450 | log("Bad options in %.100s file, line %lu: %.50s", | 439 | if (authenticated) |
451 | SSH_USER_PERMITTED_KEYS, linenum, options); | 440 | break; |
452 | packet_send_debug("Bad options in %.100s file, line %lu: %.50s", | ||
453 | SSH_USER_PERMITTED_KEYS, linenum, options); | ||
454 | authenticated = 0; | ||
455 | break; | ||
456 | |||
457 | next_option: | ||
458 | /* Skip the comma, and move to the next option (or break out | ||
459 | if there are no more). */ | ||
460 | if (!*options) | ||
461 | fatal("Bugs in auth-rsa.c option processing."); | ||
462 | if (*options == ' ' || *options == '\t') | ||
463 | break; /* End of options. */ | ||
464 | if (*options != ',') | ||
465 | goto bad_option; | ||
466 | options++; | ||
467 | /* Process the next option. */ | ||
468 | continue; | ||
469 | } | ||
470 | } | 441 | } |
471 | 442 | ||
472 | /* Break out of the loop if authentication was successful; otherwise | 443 | /* Restore the privileged uid. */ |
473 | continue searching. */ | 444 | restore_uid(); |
474 | if (authenticated) | ||
475 | break; | ||
476 | } | ||
477 | 445 | ||
478 | /* Restore the privileged uid. */ | 446 | /* Close the file. */ |
479 | restore_uid(); | 447 | fclose(f); |
480 | 448 | ||
481 | /* Close the file. */ | 449 | /* Clear any mp-int variables. */ |
482 | fclose(f); | 450 | BN_clear_free(n); |
483 | 451 | BN_clear_free(e); | |
484 | /* Clear any mp-int variables. */ | ||
485 | BN_clear_free(n); | ||
486 | BN_clear_free(e); | ||
487 | 452 | ||
488 | if (authenticated) | 453 | if (authenticated) |
489 | packet_send_debug("RSA authentication accepted."); | 454 | packet_send_debug("RSA authentication accepted."); |
490 | 455 | ||
491 | /* Return authentication result. */ | 456 | /* Return authentication result. */ |
492 | return authenticated; | 457 | return authenticated; |
493 | } | 458 | } |