linux kernel keys笔记

keys在内核中的表示为:

  struct key {
      atomic_t        usage;      /* number of references */
      key_serial_t        serial;     /* key serial number */
      union {
          struct list_head graveyard_link;
          struct rb_node  serial_node;
      };  
      struct rw_semaphore sem;        /* change vs change sem */
      struct key_user     *user;      /* owner of this key */
      void            *security;  /* security data for this key */
      union {
          time_t      expiry;     /* time at which key expires (or 0) */
          time_t      revoked_at; /* time at which key was revoked */
      };  
      time_t          last_used_at;   /* last time used for LRU keyring discard */
      kuid_t          uid;
      kgid_t          gid;
      key_perm_t      perm;       /* access permissions */
      unsigned short      quotalen;   /* length added to quota */
      unsigned short      datalen;    /* payload data length
                           * - may not match RCU dereferenced payload
                           * - payload should contain own length
                           */
      short           state;      /* Key state (+) or rejection error (-) */
      /* the key type and key description string
       * - the desc is used to match a key against search criteria
       * - it should be a printable string
       * - eg: for krb5 AFS, this might be "[email protected]"
       */
      union {
          struct keyring_index_key index_key;
          struct {
              struct key_type *type;      /* type of key */
              char        *description;
          };
      };
      /* key data
       * - this is used to hold the data actually used in cryptography or
       *   whatever
       */
      union {
          union key_payload payload;
          struct {
              /* Keyring bits */
              struct list_head name_link;
              struct assoc_array keys;
          };
      };
  
      /* This is set on a keyring to restrict the addition of a link to a key
       * to it.  If this method isn't provided then it is assumed that the
       * keyring is open to any addition.  It is ignored for non-keyring
       * keys.
       *
       * This is intended for use with rings of trusted keys whereby addition
       * to the keyring needs to be controlled.  KEY_ALLOC_BYPASS_RESTRICTION
       * overrides this, allowing the kernel to add extra keys without
       * restriction.
       */
      int (*restrict_link)(struct key *keyring,
                   const struct key_type *type,                                                                                             
                   const union key_payload *payload);
  };

(*) The key service defines three special key types:

 (+) "keyring"

 Keyrings are special keys that contain a list of other keys. Keyring
 lists can be modified using various system calls. Keyrings should not
 be given a payload when created.

 (+) "user"

 A key of this type has a description and a payload that are arbitrary
 blobs of data. These can be created, updated and read by userspace,
 and aren't intended for use by kernel services.

 (+) "logon"

 Like a "user" key, a "logon" key has a payload that is an arbitrary
 blob of data. It is intended as a place to store secrets which are
 accessible to the kernel but not to userspace programs.

 The description can be arbitrary, but must be prefixed with a non-zero
 length string that describes the key "subclass". The subclass is
 separated from the rest of the description by a ':'. "logon" keys can
 be created and updated from userspace, but the payload is only
 readable from kernel space.

这里特别需要说明的类型是keyring,它也是一种key,但是它是一种特别的key,从它的名字就能形象的看出来,它是一个钥匙圈,是一个放钥匙的容器,包含了一个链接到其他钥匙的集合。它的作用一个是方便管理,分类管理,还是一个就是增加其他key的refcount,防止变为0后key被释放掉。
user和logon key基本相同,都是由用户创建的,它们只有一点不同,logon类型的key payload在用户空间不可读,只能在内核中使用,这满足了某些安全使用需求:key加密后保存在flash或otp上,由安全部件(例如TEE)解密后加载进内核,然后这个key就可以在kernel中使用了。

系统中定义的keyring有:

Process keyrings
              Process credentials themselves reference keyrings with specific  semantics.   These
              keyrings  are pinned as long as the set of credentials exists - which is usually as
              long as the process does.

              There are three keyrings with different  inheritance/sharing  rules:   The  session
              keyring  (inherited and shared by all child processes), the process keyring (shared
              by all threads in a process) and the  thread  keyring  (specific  to  a  particular
              thread).

User keyrings
              Each  UID  known  to  the  kernel has a record that contains two keyrings: The user
              keyring and the user session keyring.  These exist for as long as the UID record in
              the  kernel  exists.  A link to the user keyring is placed in a new session keyring
              by pam_keyinit when a new login session is initiated.

Persistent keyrings
              There is a persistent keyring available to each UID known to the  system.   It  may
              persist  beyond  the  life  of  the  UID  record  previously  mentioned, but has an
              expiration time set such that it is automatically cleaned  up  after  a  set  time.
              This,  for example, permits cron scripts to use credentials left when the user logs
              out.

              Note that the expiration time is reset every time the persistent key is requested.

Special keyrings
              There are special keyrings owned by the kernel that can  anchor  keys  for  special
              purposes.   An  example  of  this is the system keyring used for holding encryption
              keys for module signature verification.

              These are usually closed to direct alteration by userspace
//security/keys/process_keys.c
key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
              key_perm_t perm)
{
      switch (id) {                                                                                          
      case KEY_SPEC_THREAD_KEYRING:                                                                          
          if (!ctx.cred->thread_keyring) {                                                                   
              if (!(lflags & KEY_LOOKUP_CREATE))                                                             
                  goto error;                                                                         
              ret = install_thread_keyring();  //不存在,则创建一个新的thread keyring                                                             
              if (ret < 0) {                                                                                 
                  key_ref = ERR_PTR(ret);                                                                    
                  goto error;                                                                                
              }                                                                                              
              goto reget_creds;                                                                              
          }                                                                                                  
                                                                                                             
          key = ctx.cred->thread_keyring;                                                                    
          __key_get(key);                                                                                    
          key_ref = make_key_ref(key, 1);                                                                    
          break;                                                                                             
      case KEY_SPEC_PROCESS_KEYRING:
          if (!ctx.cred->process_keyring) {
              if (!(lflags & KEY_LOOKUP_CREATE))
                  goto error;
  
              ret = install_process_keyring();
              if (ret < 0) {
                  key_ref = ERR_PTR(ret);
                  goto error;
              }
              goto reget_creds;
          }
  
          key = ctx.cred->process_keyring;
          __key_get(key);
          key_ref = make_key_ref(key, 1);
          break; 
      case KEY_SPEC_SESSION_KEYRING:
          if (!ctx.cred->session_keyring) {
              /* always install a session keyring upon access if one
               * doesn't exist yet */
              ret = install_user_keyrings();
              if (ret < 0)
                  goto error;
              if (lflags & KEY_LOOKUP_CREATE)
                  ret = join_session_keyring(NULL); //创建一个空的session keyring
              else
                  ret = install_session_keyring(
                      ctx.cred->user->session_keyring); //使用uid session keyring
  
              if (ret < 0)
                  goto error;
              goto reget_creds;
          } else if (ctx.cred->session_keyring ==
                 ctx.cred->user->session_keyring &&
                 lflags & KEY_LOOKUP_CREATE) {
              ret = join_session_keyring(NULL);
              if (ret < 0)
                  goto error;
              goto reget_creds;
          }
	...
}
  int install_thread_keyring_to_cred(struct cred *new)                                                       
  {                                                                                                          
      struct key *keyring;                                                                                   
                                                                                                             
      if (new->thread_keyring)                                                                               
          return 0;                                                                                          
       //注意这里thread keyring的名字                                                                                                      
      keyring = keyring_alloc("_tid", new->uid, new->gid, new,                                               
                  KEY_POS_ALL | KEY_USR_VIEW,                                                                
                  KEY_ALLOC_QUOTA_OVERRUN,                                                                   
                  NULL, NULL);                                                                               
      if (IS_ERR(keyring))                                                                                   
          return PTR_ERR(keyring);                                                                           
                                                                                                             
      new->thread_keyring = keyring;                                                                         
      return 0;                                                                                              
  }  
  int install_process_keyring_to_cred(struct cred *new)                                                      
  {                                                                                                          
      struct key *keyring;                                                                                   
                                                                                                             
      if (new->process_keyring)                                                                              
          return 0;                                                                                          
                                                                                                             
      keyring = keyring_alloc("_pid", new->uid, new->gid, new,                                               
                  KEY_POS_ALL | KEY_USR_VIEW,                                                                
                  KEY_ALLOC_QUOTA_OVERRUN,                                                                   
                  NULL, NULL);                                                                               
      if (IS_ERR(keyring))                                                                                   
          return PTR_ERR(keyring);                                                                           
                                                                                                             
      new->process_keyring = keyring;                                                                        
      return 0;                                                                                              
  }
int install_user_keyrings(void)
{
	sprintf(buf, "_uid.%u", uid);
	uid_keyring = find_keyring_by_name(buf, true);

	sprintf(buf, "_uid_ses.%u", uid);
	session_keyring = find_keyring_by_name(buf, true);

	user->uid_keyring = uid_keyring;
	user->session_keyring = session_keyring;
}

默认的,进程的cred->session_keyring和cred->user->session_keyring是指向同一块内存,都指向user session keyring,对于fork,execl的子进程,它的cred->session_keyring默认为NULL,在访问时,指向user session keyring,所以对于同一个uid,它们的cred->session_keyring是相同的,在setuid()后,它的cred->session_keyring指向新的user session keyring,如果不存在,则创建。
可以通过keyctl(KEYCTL_JOIN_SESSION_KEYRING, “_uid_ses.0”);设置进程的session_keyring为指定的keyring,不过这个指定的keyring对该进程有search的权限。

可以通过keyctl命令修改user session keyring的权限:
keyctl setperm @us 0x1f3f0b0b

@us表示user session keyring

和用户空间交互的接口都定义在security/keys/keyctl.c文件中,主要是三个接口:
add_key
request_key
keyctl
整体来说,内核文档对于keys说的比较清楚了,涉及的源码也不多,都在security/keys/目录下,结合文档看源码很容易看懂,感兴趣的可以自己去研究,这里不再啰嗦。

参考资料:
Documentation/security/keys.txt
http://manpages.ubuntu.com/manpages/bionic/man1/keyctl.1.html
http://manpages.ubuntu.com/manpages/bionic/man7/keyrings.7.html

发布了85 篇原创文章 · 获赞 26 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/whuzm08/article/details/86742300