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