[BACK]Return to auth2-pubkey.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Diff for /src/usr.bin/ssh/auth2-pubkey.c between version 1.114 and 1.115

version 1.114, 2022/05/27 05:01:25 version 1.115, 2022/05/27 05:02:46
Line 1 
Line 1 
 /* $OpenBSD$ */  /* $OpenBSD$ */
 /*  /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.   * Copyright (c) 2000 Markus Friedl.  All rights reserved.
    * Copyright (c) 2010 Damien Miller.  All rights reserved.
  *   *
  * Redistribution and use in source and binary forms, with or without   * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions   * modification, are permitted provided that the following conditions
Line 25 
Line 26 
   
   
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/stat.h>  
   
 #include <stdlib.h>  #include <stdlib.h>
 #include <errno.h>  #include <errno.h>
 #include <fcntl.h>  
 #include <paths.h>  #include <paths.h>
 #include <pwd.h>  #include <pwd.h>
 #include <signal.h>  #include <signal.h>
Line 64 
Line 63 
 #include "authfile.h"  #include "authfile.h"
 #include "match.h"  #include "match.h"
 #include "ssherr.h"  #include "ssherr.h"
 #include "kex.h"  
 #include "channels.h" /* XXX for session.h */  #include "channels.h" /* XXX for session.h */
 #include "session.h" /* XXX for child_set_env(); refactor? */  #include "session.h" /* XXX for child_set_env(); refactor? */
 #include "sk-api.h"  #include "sk-api.h"
Line 319 
Line 317 
 }  }
   
 static int  static int
 match_principals_option(const char *principal_list, struct sshkey_cert *cert)  
 {  
         char *result;  
         u_int i;  
   
         /* XXX percent_expand() sequences for authorized_principals? */  
   
         for (i = 0; i < cert->nprincipals; i++) {  
                 if ((result = match_list(cert->principals[i],  
                     principal_list, NULL)) != NULL) {  
                         debug3("matched principal from key options \"%.100s\"",  
                             result);  
                         free(result);  
                         return 1;  
                 }  
         }  
         return 0;  
 }  
   
 /*  
  * Process a single authorized_principals format line. Returns 0 and sets  
  * authoptsp is principal is authorised, -1 otherwise. "loc" is used as a  
  * log preamble for file/line information.  
  */  
 static int  
 check_principals_line(char *cp, const struct sshkey_cert *cert,  
     const char *loc, struct sshauthopt **authoptsp)  
 {  
         u_int i, found = 0;  
         char *ep, *line_opts;  
         const char *reason = NULL;  
         struct sshauthopt *opts = NULL;  
   
         if (authoptsp != NULL)  
                 *authoptsp = NULL;  
   
         /* Trim trailing whitespace. */  
         ep = cp + strlen(cp) - 1;  
         while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))  
                 *ep-- = '\0';  
   
         /*  
          * If the line has internal whitespace then assume it has  
          * key options.  
          */  
         line_opts = NULL;  
         if ((ep = strrchr(cp, ' ')) != NULL ||  
             (ep = strrchr(cp, '\t')) != NULL) {  
                 for (; *ep == ' ' || *ep == '\t'; ep++)  
                         ;  
                 line_opts = cp;  
                 cp = ep;  
         }  
         if ((opts = sshauthopt_parse(line_opts, &reason)) == NULL) {  
                 debug("%s: bad principals options: %s", loc, reason);  
                 auth_debug_add("%s: bad principals options: %s", loc, reason);  
                 return -1;  
         }  
         /* Check principals in cert against those on line */  
         for (i = 0; i < cert->nprincipals; i++) {  
                 if (strcmp(cp, cert->principals[i]) != 0)  
                         continue;  
                 debug3("%s: matched principal \"%.100s\"",  
                     loc, cert->principals[i]);  
                 found = 1;  
         }  
         if (found && authoptsp != NULL) {  
                 *authoptsp = opts;  
                 opts = NULL;  
         }  
         sshauthopt_free(opts);  
         return found ? 0 : -1;  
 }  
   
 static int  
 process_principals(FILE *f, const char *file,  
     const struct sshkey_cert *cert, struct sshauthopt **authoptsp)  
 {  
         char loc[256], *line = NULL, *cp, *ep;  
         size_t linesize = 0;  
         u_long linenum = 0, nonblank = 0;  
         u_int found_principal = 0;  
   
         if (authoptsp != NULL)  
                 *authoptsp = NULL;  
   
         while (getline(&line, &linesize, f) != -1) {  
                 linenum++;  
                 /* Always consume entire input */  
                 if (found_principal)  
                         continue;  
   
                 /* Skip leading whitespace. */  
                 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)  
                         ;  
                 /* Skip blank and comment lines. */  
                 if ((ep = strchr(cp, '#')) != NULL)  
                         *ep = '\0';  
                 if (!*cp || *cp == '\n')  
                         continue;  
   
                 nonblank++;  
                 snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);  
                 if (check_principals_line(cp, cert, loc, authoptsp) == 0)  
                         found_principal = 1;  
         }  
         debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum);  
         free(line);  
         return found_principal;  
 }  
   
 /* XXX remove pw args here and elsewhere once ssh->authctxt is guaranteed */  
   
 static int  
 match_principals_file(struct passwd *pw, char *file,  match_principals_file(struct passwd *pw, char *file,
     struct sshkey_cert *cert, struct sshauthopt **authoptsp)      struct sshkey_cert *cert, struct sshauthopt **authoptsp)
 {  {
Line 448 
Line 332 
                 restore_uid();                  restore_uid();
                 return 0;                  return 0;
         }          }
         success = process_principals(f, file, cert, authoptsp);          success = auth_process_principals(f, file, cert, authoptsp);
         fclose(f);          fclose(f);
         restore_uid();          restore_uid();
         return success;          return success;
Line 564 
Line 448 
         uid_swapped = 1;          uid_swapped = 1;
         temporarily_use_uid(runas_pw);          temporarily_use_uid(runas_pw);
   
         ok = process_principals(f, "(command)", cert, authoptsp);          ok = auth_process_principals(f, "(command)", cert, authoptsp);
   
         fclose(f);          fclose(f);
         f = NULL;          f = NULL;
Line 592 
Line 476 
         return found_principal;          return found_principal;
 }  }
   
 /*  
  * Check a single line of an authorized_keys-format file. Returns 0 if key  
  * matches, -1 otherwise. Will return key/cert options via *authoptsp  
  * on success. "loc" is used as file/line location in log messages.  
  */  
 static int  
 check_authkey_line(struct passwd *pw, struct sshkey *key,  
     char *cp, const char *remote_ip, const char *remote_host, const char *loc,  
     struct sshauthopt **authoptsp)  
 {  
         int want_keytype = sshkey_is_cert(key) ? KEY_UNSPEC : key->type;  
         struct sshkey *found = NULL;  
         struct sshauthopt *keyopts = NULL, *certopts = NULL, *finalopts = NULL;  
         char *key_options = NULL, *fp = NULL;  
         const char *reason = NULL;  
         int ret = -1;  
   
         if (authoptsp != NULL)  
                 *authoptsp = NULL;  
   
         if ((found = sshkey_new(want_keytype)) == NULL) {  
                 debug3_f("keytype %d failed", want_keytype);  
                 goto out;  
         }  
   
         /* XXX djm: peek at key type in line and skip if unwanted */  
   
         if (sshkey_read(found, &cp) != 0) {  
                 /* no key?  check for options */  
                 debug2("%s: check options: '%s'", loc, cp);  
                 key_options = cp;  
                 if (sshkey_advance_past_options(&cp) != 0) {  
                         reason = "invalid key option string";  
                         goto fail_reason;  
                 }  
                 skip_space(&cp);  
                 if (sshkey_read(found, &cp) != 0) {  
                         /* still no key?  advance to next line*/  
                         debug2("%s: advance: '%s'", loc, cp);  
                         goto out;  
                 }  
         }  
         /* Parse key options now; we need to know if this is a CA key */  
         if ((keyopts = sshauthopt_parse(key_options, &reason)) == NULL) {  
                 debug("%s: bad key options: %s", loc, reason);  
                 auth_debug_add("%s: bad key options: %s", loc, reason);  
                 goto out;  
         }  
         /* Ignore keys that don't match or incorrectly marked as CAs */  
         if (sshkey_is_cert(key)) {  
                 /* Certificate; check signature key against CA */  
                 if (!sshkey_equal(found, key->cert->signature_key) ||  
                     !keyopts->cert_authority)  
                         goto out;  
         } else {  
                 /* Plain key: check it against key found in file */  
                 if (!sshkey_equal(found, key) || keyopts->cert_authority)  
                         goto out;  
         }  
   
         /* We have a candidate key, perform authorisation checks */  
         if ((fp = sshkey_fingerprint(found,  
             options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)  
                 fatal_f("fingerprint failed");  
   
         debug("%s: matching %s found: %s %s", loc,  
             sshkey_is_cert(key) ? "CA" : "key", sshkey_type(found), fp);  
   
         if (auth_authorise_keyopts(pw, keyopts,  
             sshkey_is_cert(key), remote_ip, remote_host, loc) != 0) {  
                 reason = "Refused by key options";  
                 goto fail_reason;  
         }  
         /* That's all we need for plain keys. */  
         if (!sshkey_is_cert(key)) {  
                 verbose("Accepted key %s %s found at %s",  
                     sshkey_type(found), fp, loc);  
                 finalopts = keyopts;  
                 keyopts = NULL;  
                 goto success;  
         }  
   
         /*  
          * Additional authorisation for certificates.  
          */  
   
         /* Parse and check options present in certificate */  
         if ((certopts = sshauthopt_from_cert(key)) == NULL) {  
                 reason = "Invalid certificate options";  
                 goto fail_reason;  
         }  
         if (auth_authorise_keyopts(pw, certopts, 0,  
             remote_ip, remote_host, loc) != 0) {  
                 reason = "Refused by certificate options";  
                 goto fail_reason;  
         }  
         if ((finalopts = sshauthopt_merge(keyopts, certopts, &reason)) == NULL)  
                 goto fail_reason;  
   
         /*  
          * If the user has specified a list of principals as  
          * a key option, then prefer that list to matching  
          * their username in the certificate principals list.  
          */  
         if (keyopts->cert_principals != NULL &&  
             !match_principals_option(keyopts->cert_principals, key->cert)) {  
                 reason = "Certificate does not contain an authorized principal";  
                 goto fail_reason;  
         }  
         if (sshkey_cert_check_authority_now(key, 0, 0, 0,  
             keyopts->cert_principals == NULL ? pw->pw_name : NULL,  
             &reason) != 0)  
                 goto fail_reason;  
   
         verbose("Accepted certificate ID \"%s\" (serial %llu) "  
             "signed by CA %s %s found at %s",  
             key->cert->key_id,  
             (unsigned long long)key->cert->serial,  
             sshkey_type(found), fp, loc);  
   
  success:  
         if (finalopts == NULL)  
                 fatal_f("internal error: missing options");  
         if (authoptsp != NULL) {  
                 *authoptsp = finalopts;  
                 finalopts = NULL;  
         }  
         /* success */  
         ret = 0;  
         goto out;  
   
  fail_reason:  
         error("%s", reason);  
         auth_debug_add("%s", reason);  
  out:  
         free(fp);  
         sshauthopt_free(keyopts);  
         sshauthopt_free(certopts);  
         sshauthopt_free(finalopts);  
         sshkey_free(found);  
         return ret;  
 }  
   
 /*  
  * Checks whether key is allowed in authorized_keys-format file,  
  * returns 1 if the key is allowed or 0 otherwise.  
  */  
 static int  
 check_authkeys_file(struct passwd *pw, FILE *f, char *file,  
     struct sshkey *key, const char *remote_ip,  
     const char *remote_host, struct sshauthopt **authoptsp)  
 {  
         char *cp, *line = NULL, loc[256];  
         size_t linesize = 0;  
         int found_key = 0;  
         u_long linenum = 0, nonblank = 0;  
   
         if (authoptsp != NULL)  
                 *authoptsp = NULL;  
   
         while (getline(&line, &linesize, f) != -1) {  
                 linenum++;  
                 /* Always consume entire file */  
                 if (found_key)  
                         continue;  
   
                 /* Skip leading whitespace, empty and comment lines. */  
                 cp = line;  
                 skip_space(&cp);  
                 if (!*cp || *cp == '\n' || *cp == '#')  
                         continue;  
   
                 nonblank++;  
                 snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);  
                 if (check_authkey_line(pw, key, cp,  
                     remote_ip, remote_host, loc, authoptsp) == 0)  
                         found_key = 1;  
         }  
         free(line);  
         debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum);  
         return found_key;  
 }  
   
 /* Authenticate a certificate key against TrustedUserCAKeys */  /* Authenticate a certificate key against TrustedUserCAKeys */
 static int  static int
 user_cert_trusted_ca(struct passwd *pw, struct sshkey *key,  user_cert_trusted_ca(struct passwd *pw, struct sshkey *key,
Line 899 
Line 600 
   
         debug("trying public key file %s", file);          debug("trying public key file %s", file);
         if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {          if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
                 found_key = check_authkeys_file(pw, f, file,                  found_key = auth_check_authkeys_file(pw, f, file,
                     key, remote_ip, remote_host, authoptsp);                      key, remote_ip, remote_host, authoptsp);
                 fclose(f);                  fclose(f);
         }          }
Line 1015 
Line 716 
         uid_swapped = 1;          uid_swapped = 1;
         temporarily_use_uid(runas_pw);          temporarily_use_uid(runas_pw);
   
         ok = check_authkeys_file(user_pw, f,          ok = auth_check_authkeys_file(user_pw, f,
             options.authorized_keys_command, key, remote_ip,              options.authorized_keys_command, key, remote_ip,
             remote_host, authoptsp);              remote_host, authoptsp);
   

Legend:
Removed from v.1.114  
changed lines
  Added in v.1.115