]> andersk Git - openssh.git/blob - auth-rsa.c
Merged OpenBSD CVS changes that go away
[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 "config.h"
19 #include "includes.h"
20 RCSID("$Id$");
21
22 #include "rsa.h"
23 #include "packet.h"
24 #include "xmalloc.h"
25 #include "ssh.h"
26 #include "mpaux.h"
27 #include "uidswap.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(unsigned int bits, 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
104   memset(buf, 0, 32);
105   BN_bn2bin(challenge, buf + 32 - len);
106   MD5_Init(&md);
107   MD5_Update(&md, buf, 32);
108   MD5_Update(&md, session_id, 16);
109   MD5_Final(mdbuf, &md);
110
111   /* We will no longer need these. */
112   BN_clear_free(encrypted_challenge);
113   BN_clear_free(challenge);
114   BN_clear_free(aux);
115   BN_CTX_free(ctx);
116   
117   /* Wait for a response. */
118   packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE);
119   packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE);
120   for (i = 0; i < 16; i++)
121     response[i] = packet_get_char();
122
123   /* Verify that the response is the original challenge. */
124   if (memcmp(response, mdbuf, 16) != 0)
125     {
126       /* Wrong answer. */
127       return 0;
128     }
129
130   /* Correct answer. */
131   return 1;
132 }
133
134 /* Performs the RSA authentication dialog with the client.  This returns
135    0 if the client could not be authenticated, and 1 if authentication was
136    successful.  This may exit if there is a serious protocol violation. */
137
138 int
139 auth_rsa(struct passwd *pw, BIGNUM *client_n, int strict_modes)
140 {
141   char line[8192];
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(line, sizeof line, "%.500s/%.100s", pw->pw_dir,
154     SSH_USER_PERMITTED_KEYS);
155   
156   /* Fail quietly if file does not exist */
157   if (stat(line, &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(line, "r");
166   if (!f)
167     {
168       /* Restore the privileged uid. */
169       restore_uid();
170       packet_send_debug("Could not open %.900s for reading.", line);
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 (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, line);
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 if the we have found the desired key (identified by its
267          modulus). */
268       if (BN_cmp(n, client_n) != 0)
269         continue; /* Wrong key. */
270
271       /* We have found the desired key. */
272
273       /* Perform the challenge-response dialog for this key. */
274       if (!auth_rsa_challenge_dialog(bits, e, n))
275         {
276           /* Wrong response. */
277           log("Wrong response to RSA authentication challenge.");
278           packet_send_debug("Wrong response to RSA authentication challenge.");
279           continue;
280         }
281
282       /* Correct response.  The client has been successfully authenticated.
283          Note that we have not yet processed the options; this will be reset
284          if the options cause the authentication to be rejected. */
285       authenticated = 1;
286
287       /* RSA part of authentication was accepted.  Now process the options. */
288       if (options)
289         {
290           while (*options && *options != ' ' && *options != '\t')
291             {
292               cp = "no-port-forwarding";
293               if (strncmp(options, cp, strlen(cp)) == 0)
294                 {
295                   packet_send_debug("Port forwarding disabled.");
296                   no_port_forwarding_flag = 1;
297                   options += strlen(cp);
298                   goto next_option;
299                 }
300               cp = "no-agent-forwarding";
301               if (strncmp(options, cp, strlen(cp)) == 0)
302                 {
303                   packet_send_debug("Agent forwarding disabled.");
304                   no_agent_forwarding_flag = 1;
305                   options += strlen(cp);
306                   goto next_option;
307                 }
308               cp = "no-X11-forwarding";
309               if (strncmp(options, cp, strlen(cp)) == 0)
310                 {
311                   packet_send_debug("X11 forwarding disabled.");
312                   no_x11_forwarding_flag = 1;
313                   options += strlen(cp);
314                   goto next_option;
315                 }
316               cp = "no-pty";
317               if (strncmp(options, cp, strlen(cp)) == 0)
318                 {
319                   packet_send_debug("Pty allocation disabled.");
320                   no_pty_flag = 1;
321                   options += strlen(cp);
322                   goto next_option;
323                 }
324               cp = "command=\"";
325               if (strncmp(options, cp, strlen(cp)) == 0)
326                 {
327                   int i;
328                   options += strlen(cp);
329                   forced_command = xmalloc(strlen(options) + 1);
330                   i = 0;
331                   while (*options)
332                     {
333                       if (*options == '"')
334                         break;
335                       if (*options == '\\' && options[1] == '"')
336                         {
337                           options += 2;
338                           forced_command[i++] = '"';
339                           continue;
340                         }
341                       forced_command[i++] = *options++;
342                     }
343                   if (!*options)
344                     {
345                       debug("%.100s, line %lu: missing end quote",
346                             SSH_USER_PERMITTED_KEYS, linenum);
347                       packet_send_debug("%.100s, line %lu: missing end quote",
348                                         SSH_USER_PERMITTED_KEYS, linenum);
349                       continue;
350                     }
351                   forced_command[i] = 0;
352                   packet_send_debug("Forced command: %.900s", forced_command);
353                   options++;
354                   goto next_option;
355                 }
356               cp = "environment=\"";
357               if (strncmp(options, cp, strlen(cp)) == 0)
358                 {
359                   int i;
360                   char *s;
361                   struct envstring *new_envstring;
362                   options += strlen(cp);
363                   s = xmalloc(strlen(options) + 1);
364                   i = 0;
365                   while (*options)
366                     {
367                       if (*options == '"')
368                         break;
369                       if (*options == '\\' && options[1] == '"')
370                         {
371                           options += 2;
372                           s[i++] = '"';
373                           continue;
374                         }
375                       s[i++] = *options++;
376                     }
377                   if (!*options)
378                     {
379                       debug("%.100s, line %lu: missing end quote",
380                             SSH_USER_PERMITTED_KEYS, linenum);
381                       packet_send_debug("%.100s, line %lu: missing end quote",
382                                         SSH_USER_PERMITTED_KEYS, linenum);
383                       continue;
384                     }
385                   s[i] = 0;
386                   packet_send_debug("Adding to environment: %.900s", s);
387                   debug("Adding to environment: %.900s", s);
388                   options++;
389                   new_envstring = xmalloc(sizeof(struct envstring));
390                   new_envstring->s = s;
391                   new_envstring->next = custom_environment;
392                   custom_environment = new_envstring;
393                   goto next_option;
394                 }
395               cp = "from=\"";
396               if (strncmp(options, cp, strlen(cp)) == 0)
397                 {
398                   char *patterns = xmalloc(strlen(options) + 1);
399                   int i;
400                   options += strlen(cp);
401                   i = 0;
402                   while (*options)
403                     {
404                       if (*options == '"')
405                         break;
406                       if (*options == '\\' && options[1] == '"')
407                         {
408                           options += 2;
409                           patterns[i++] = '"';
410                           continue;
411                         }
412                       patterns[i++] = *options++;
413                     }
414                   if (!*options)
415                     {
416                       debug("%.100s, line %lu: missing end quote",
417                             SSH_USER_PERMITTED_KEYS, linenum);
418                       packet_send_debug("%.100s, line %lu: missing end quote",
419                                         SSH_USER_PERMITTED_KEYS, linenum);
420                       continue;
421                     }
422                   patterns[i] = 0;
423                   options++;
424                   if (!match_hostname(get_canonical_hostname(), patterns,
425                                      strlen(patterns)) &&
426                       !match_hostname(get_remote_ipaddr(), patterns,
427                                       strlen(patterns)))
428                     {
429                       log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).",
430                           pw->pw_name, get_canonical_hostname(),
431                           get_remote_ipaddr());
432                       packet_send_debug("Your host '%.200s' is not permitted to use this key for login.",
433                                         get_canonical_hostname());
434                       xfree(patterns);
435                       authenticated = 0;
436                       break;
437                     }
438                   xfree(patterns);
439                   /* Host name matches. */
440                   goto next_option;
441                 }
442             bad_option:
443               /* Unknown option. */
444               log("Bad options in %.100s file, line %lu: %.50s",
445                   SSH_USER_PERMITTED_KEYS, linenum, options);
446               packet_send_debug("Bad options in %.100s file, line %lu: %.50s",
447                                 SSH_USER_PERMITTED_KEYS, linenum, options);
448               authenticated = 0;
449               break;
450
451             next_option:
452               /* Skip the comma, and move to the next option (or break out
453                  if there are no more). */
454               if (!*options)
455                 fatal("Bugs in auth-rsa.c option processing.");
456               if (*options == ' ' || *options == '\t')
457                 break; /* End of options. */
458               if (*options != ',')
459                 goto bad_option;
460               options++;
461               /* Process the next option. */
462               continue;
463             }
464         }
465
466       /* Break out of the loop if authentication was successful; otherwise
467          continue searching. */
468       if (authenticated)
469         break;
470     }
471
472   /* Restore the privileged uid. */
473   restore_uid();
474
475   /* Close the file. */
476   fclose(f);
477   
478   /* Clear any mp-int variables. */
479   BN_clear_free(n);
480   BN_clear_free(e);
481
482   if (authenticated)
483     packet_send_debug("RSA authentication accepted.");
484
485   /* Return authentication result. */
486   return authenticated;
487 }
This page took 0.077606 seconds and 5 git commands to generate.