]> andersk Git - openssh.git/blob - auth-rsa.c
- Merged very large OpenBSD source code reformat
[openssh.git] / auth-rsa.c
1 /*
2  * 
3  * auth-rsa.c
4  * 
5  * Author: Tatu Ylonen <ylo@cs.hut.fi>
6  * 
7  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
8  *                    All rights reserved
9  * 
10  * Created: Mon Mar 27 01:46:52 1995 ylo
11  * 
12  * RSA-based authentication.  This code determines whether to admit a login
13  * based on RSA authentication.  This file also contains functions to check
14  * validity of the host key.
15  * 
16  */
17
18 #include "includes.h"
19 RCSID("$Id$");
20
21 #include "rsa.h"
22 #include "packet.h"
23 #include "xmalloc.h"
24 #include "ssh.h"
25 #include "mpaux.h"
26 #include "uidswap.h"
27 #include "servconf.h"
28
29 #ifdef HAVE_OPENSSL
30 #include <openssl/rsa.h>
31 #include <openssl/md5.h>
32 #endif
33 #ifdef HAVE_SSL
34 #include <ssl/rsa.h>
35 #include <ssl/md5.h>
36 #endif
37
38 /* Flags that may be set in authorized_keys options. */
39 extern int no_port_forwarding_flag;
40 extern int no_agent_forwarding_flag;
41 extern int no_x11_forwarding_flag;
42 extern int no_pty_flag;
43 extern char *forced_command;
44 extern struct envstring *custom_environment;
45
46 /* Session identifier that is used to bind key exchange and authentication
47    responses to a particular session. */
48 extern unsigned char session_id[16];
49
50 /* The .ssh/authorized_keys file contains public keys, one per line, in the
51    following format:
52      options bits e n comment
53    where bits, e and n are decimal numbers,
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
56    description of the options.
57 */
58
59 /* Performs the RSA authentication challenge-response dialog with the client,
60    and returns true (non-zero) if the client gave the correct answer to
61    our challenge; returns zero if the client gives a wrong answer. */
62
63 int
64 auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n)
65 {
66         BIGNUM *challenge, *encrypted_challenge, *aux;
67         RSA *pk;
68         BN_CTX *ctx = BN_CTX_new();
69         unsigned char buf[32], mdbuf[16], response[16];
70         MD5_CTX md;
71         unsigned int i;
72         int plen, len;
73
74         encrypted_challenge = BN_new();
75         challenge = BN_new();
76         aux = BN_new();
77
78         /* Generate a random challenge. */
79         BN_rand(challenge, 256, 0, 0);
80         BN_mod(challenge, challenge, n, ctx);
81
82         /* Create the public key data structure. */
83         pk = RSA_new();
84         pk->e = BN_new();
85         BN_copy(pk->e, e);
86         pk->n = BN_new();
87         BN_copy(pk->n, n);
88
89         /* Encrypt the challenge with the public key. */
90         rsa_public_encrypt(encrypted_challenge, challenge, pk);
91         RSA_free(pk);
92
93         /* Send the encrypted challenge to the client. */
94         packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
95         packet_put_bignum(encrypted_challenge);
96         packet_send();
97         packet_write_wait();
98
99         /* The response is MD5 of decrypted challenge plus session id. */
100         len = BN_num_bytes(challenge);
101         if (len <= 0 || len > 32)
102                 fatal("auth_rsa_challenge_dialog: bad challenge length %d", len);
103         memset(buf, 0, 32);
104         BN_bn2bin(challenge, buf + 32 - len);
105         MD5_Init(&md);
106         MD5_Update(&md, buf, 32);
107         MD5_Update(&md, session_id, 16);
108         MD5_Final(mdbuf, &md);
109
110         /* We will no longer need these. */
111         BN_clear_free(encrypted_challenge);
112         BN_clear_free(challenge);
113         BN_clear_free(aux);
114         BN_CTX_free(ctx);
115
116         /* Wait for a response. */
117         packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE);
118         packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE);
119         for (i = 0; i < 16; i++)
120                 response[i] = packet_get_char();
121
122         /* Verify that the response is the original challenge. */
123         if (memcmp(response, mdbuf, 16) != 0) {
124                 /* Wrong answer. */
125                 return 0;
126         }
127         /* Correct answer. */
128         return 1;
129 }
130
131 /* Performs the RSA authentication dialog with the client.  This returns
132    0 if the client could not be authenticated, and 1 if authentication was
133    successful.  This may exit if there is a serious protocol violation. */
134
135 int
136 auth_rsa(struct passwd *pw, BIGNUM *client_n)
137 {
138         extern ServerOptions options;
139         char line[8192], file[1024];
140         int authenticated;
141         unsigned int bits;
142         FILE *f;
143         unsigned long linenum = 0;
144         struct stat st;
145         BIGNUM *e, *n;
146
147         /* Temporarily use the user's uid. */
148         temporarily_use_uid(pw->pw_uid);
149
150         /* The authorized keys. */
151         snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
152                  SSH_USER_PERMITTED_KEYS);
153
154         /* Fail quietly if file does not exist */
155         if (stat(file, &st) < 0) {
156                 /* Restore the privileged uid. */
157                 restore_uid();
158                 return 0;
159         }
160         /* Open the file containing the authorized keys. */
161         f = fopen(file, "r");
162         if (!f) {
163                 /* Restore the privileged uid. */
164                 restore_uid();
165                 packet_send_debug("Could not open %.900s for reading.", file);
166                 packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
167                 return 0;
168         }
169         if (options.strict_modes) {
170                 int fail = 0;
171                 char buf[1024];
172                 /* Check open file in order to avoid open/stat races */
173                 if (fstat(fileno(f), &st) < 0 ||
174                     (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
175                     (st.st_mode & 022) != 0) {
176                         snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: "
177                                  "bad ownership or modes for '%s'.", pw->pw_name, file);
178                         fail = 1;
179                 } else {
180                         /* Check path to SSH_USER_PERMITTED_KEYS */
181                         int i;
182                         static const char *check[] = {
183                                 "", SSH_USER_DIR, NULL
184                         };
185                         for (i = 0; check[i]; i++) {
186                                 snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]);
187                                 if (stat(line, &st) < 0 ||
188                                     (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
189                                     (st.st_mode & 022) != 0) {
190                                         snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: "
191                                                  "bad ownership or modes for '%s'.", pw->pw_name, line);
192                                         fail = 1;
193                                         break;
194                                 }
195                         }
196                 }
197                 if (fail) {
198                         log(buf);
199                         packet_send_debug(buf);
200                         restore_uid();
201                         return 0;
202                 }
203         }
204         /* Flag indicating whether authentication has succeeded. */
205         authenticated = 0;
206
207         /* Initialize mp-int variables. */
208         e = BN_new();
209         n = BN_new();
210
211         /* Go though the accepted keys, looking for the current key.  If
212            found, perform a challenge-response dialog to verify that the
213            user really has the corresponding private key. */
214         while (fgets(line, sizeof(line), f)) {
215                 char *cp;
216                 char *options;
217
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;
239                         }
240                 } else
241                         options = NULL;
242
243                 /* Parse the key from the line. */
244                 if (!auth_rsa_read_key(&cp, &bits, e, n)) {
245                         debug("%.100s, line %lu: bad key syntax",
246                               SSH_USER_PERMITTED_KEYS, linenum);
247                         packet_send_debug("%.100s, line %lu: bad key syntax",
248                                           SSH_USER_PERMITTED_KEYS, linenum);
249                         continue;
250                 }
251                 /* cp now points to the comment part. */
252
253                 /* check the real bits  */
254                 if (bits != BN_num_bits(n))
255                         error("Warning: error in %s, line %ld: keysize mismatch: "
256                               "actual size %d vs. announced %d.",
257                               file, linenum, BN_num_bits(n), bits);
258
259                 /* Check if the we have found the desired key (identified by its modulus). */
260                 if (BN_cmp(n, client_n) != 0)
261                         continue;       /* Wrong key. */
262
263                 /* We have found the desired key. */
264
265                 /* Perform the challenge-response dialog for this key. */
266                 if (!auth_rsa_challenge_dialog(e, n)) {
267                         /* Wrong response. */
268                         verbose("Wrong response to RSA authentication challenge.");
269                         packet_send_debug("Wrong response to RSA authentication challenge.");
270                         continue;
271                 }
272                 /* Correct response.  The client has been successfully
273                    authenticated. Note that we have not yet processed the
274                    options; this will be reset if the options cause the
275                    authentication to be rejected. */
276                 authenticated = 1;
277
278                 /* RSA part of authentication was accepted.  Now process the options. */
279                 if (options) {
280                         while (*options && *options != ' ' && *options != '\t') {
281                                 cp = "no-port-forwarding";
282                                 if (strncmp(options, cp, strlen(cp)) == 0) {
283                                         packet_send_debug("Port forwarding disabled.");
284                                         no_port_forwarding_flag = 1;
285                                         options += strlen(cp);
286                                         goto next_option;
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;
435                         }
436                 }
437                 /* Break out of the loop if authentication was successful;
438                    otherwise continue searching. */
439                 if (authenticated)
440                         break;
441         }
442
443         /* Restore the privileged uid. */
444         restore_uid();
445
446         /* Close the file. */
447         fclose(f);
448
449         /* Clear any mp-int variables. */
450         BN_clear_free(n);
451         BN_clear_free(e);
452
453         if (authenticated)
454                 packet_send_debug("RSA authentication accepted.");
455
456         /* Return authentication result. */
457         return authenticated;
458 }
This page took 0.07721 seconds and 5 git commands to generate.