]> andersk Git - openssh.git/blob - auth-rsa.c
- Merged OpenBSD CVS changes:
[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     {
125       /* Wrong answer. */
126       return 0;
127     }
128
129   /* Correct answer. */
130   return 1;
131 }
132
133 /* Performs the RSA authentication dialog with the client.  This returns
134    0 if the client could not be authenticated, and 1 if authentication was
135    successful.  This may exit if there is a serious protocol violation. */
136
137 int
138 auth_rsa(struct passwd *pw, BIGNUM *client_n)
139 {
140   extern ServerOptions options;
141   char line[8192], file[1024];
142   int authenticated;
143   unsigned int bits;
144   FILE *f;
145   unsigned long linenum = 0;
146   struct stat st;
147   BIGNUM *e, *n;
148
149   /* Temporarily use the user's uid. */
150   temporarily_use_uid(pw->pw_uid);
151
152   /* The authorized keys. */
153   snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
154     SSH_USER_PERMITTED_KEYS);
155   
156   /* Fail quietly if file does not exist */
157   if (stat(file, &st) < 0)
158     {
159       /* Restore the privileged uid. */
160       restore_uid();
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         }
252       else
253         options = NULL;
254       
255       /* Parse the key from the line. */
256       if (!auth_rsa_read_key(&cp, &bits, e, n))
257         {
258           debug("%.100s, line %lu: bad key syntax", 
259                 SSH_USER_PERMITTED_KEYS, linenum);
260           packet_send_debug("%.100s, line %lu: bad key syntax", 
261                             SSH_USER_PERMITTED_KEYS, linenum);
262           continue;
263         }
264       /* cp now points to the comment part. */
265
266       /* check the real bits  */
267       if (bits != BN_num_bits(n))
268         error("Warning: error in %s, line %d: keysize mismatch: "
269               "actual size %d vs. announced %d.",
270               file, linenum, BN_num_bits(n), bits);
271
272       /* Check if the we have found the desired key (identified by its
273          modulus). */
274       if (BN_cmp(n, client_n) != 0)
275         continue; /* Wrong key. */
276
277       /* We have found the desired key. */
278
279       /* Perform the challenge-response dialog for this key. */
280       if (!auth_rsa_challenge_dialog(e, n))
281         {
282           /* Wrong response. */
283           log("Wrong response to RSA authentication challenge.");
284           packet_send_debug("Wrong response to RSA authentication challenge.");
285           continue;
286         }
287
288       /* Correct response.  The client has been successfully authenticated.
289          Note that we have not yet processed the options; this will be reset
290          if the options cause the authentication to be rejected. */
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                 }
322               cp = "no-pty";
323               if (strncmp(options, cp, strlen(cp)) == 0)
324                 {
325                   packet_send_debug("Pty allocation disabled.");
326                   no_pty_flag = 1;
327                   options += strlen(cp);
328                   goto next_option;
329                 }
330               cp = "command=\"";
331               if (strncmp(options, cp, strlen(cp)) == 0)
332                 {
333                   int i;
334                   options += strlen(cp);
335                   forced_command = xmalloc(strlen(options) + 1);
336                   i = 0;
337                   while (*options)
338                     {
339                       if (*options == '"')
340                         break;
341                       if (*options == '\\' && options[1] == '"')
342                         {
343                           options += 2;
344                           forced_command[i++] = '"';
345                           continue;
346                         }
347                       forced_command[i++] = *options++;
348                     }
349                   if (!*options)
350                     {
351                       debug("%.100s, line %lu: missing end quote",
352                             SSH_USER_PERMITTED_KEYS, linenum);
353                       packet_send_debug("%.100s, line %lu: missing end quote",
354                                         SSH_USER_PERMITTED_KEYS, linenum);
355                       continue;
356                     }
357                   forced_command[i] = 0;
358                   packet_send_debug("Forced command: %.900s", forced_command);
359                   options++;
360                   goto next_option;
361                 }
362               cp = "environment=\"";
363               if (strncmp(options, cp, strlen(cp)) == 0)
364                 {
365                   int i;
366                   char *s;
367                   struct envstring *new_envstring;
368                   options += strlen(cp);
369                   s = xmalloc(strlen(options) + 1);
370                   i = 0;
371                   while (*options)
372                     {
373                       if (*options == '"')
374                         break;
375                       if (*options == '\\' && options[1] == '"')
376                         {
377                           options += 2;
378                           s[i++] = '"';
379                           continue;
380                         }
381                       s[i++] = *options++;
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                 }
401               cp = "from=\"";
402               if (strncmp(options, cp, strlen(cp)) == 0)
403                 {
404                   char *patterns = xmalloc(strlen(options) + 1);
405                   int i;
406                   options += strlen(cp);
407                   i = 0;
408                   while (*options)
409                     {
410                       if (*options == '"')
411                         break;
412                       if (*options == '\\' && options[1] == '"')
413                         {
414                           options += 2;
415                           patterns[i++] = '"';
416                           continue;
417                         }
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                 }
448             bad_option:
449               /* Unknown option. */
450               log("Bad options in %.100s file, line %lu: %.50s",
451                   SSH_USER_PERMITTED_KEYS, linenum, options);
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         }
471
472       /* Break out of the loop if authentication was successful; otherwise
473          continue searching. */
474       if (authenticated)
475         break;
476     }
477
478   /* Restore the privileged uid. */
479   restore_uid();
480
481   /* Close the file. */
482   fclose(f);
483   
484   /* Clear any mp-int variables. */
485   BN_clear_free(n);
486   BN_clear_free(e);
487
488   if (authenticated)
489     packet_send_debug("RSA authentication accepted.");
490
491   /* Return authentication result. */
492   return authenticated;
493 }
This page took 0.119805 seconds and 5 git commands to generate.