diff options
Diffstat (limited to 'security/keys/keyctl.c')
| -rw-r--r-- | security/keys/keyctl.c | 161 | 
1 files changed, 139 insertions, 22 deletions
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 7f09fb897d2..74c96852459 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -103,7 +103,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,  	}  	/* find the target keyring (which must be writable) */ -	keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE); +	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref);  		goto error3; @@ -185,7 +185,8 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,  	/* get the destination keyring if specified */  	dest_ref = NULL;  	if (destringid) { -		dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE); +		dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE, +					   KEY_WRITE);  		if (IS_ERR(dest_ref)) {  			ret = PTR_ERR(dest_ref);  			goto error3; @@ -233,9 +234,11 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,  long keyctl_get_keyring_ID(key_serial_t id, int create)  {  	key_ref_t key_ref; +	unsigned long lflags;  	long ret; -	key_ref = lookup_user_key(id, create, 0, KEY_SEARCH); +	lflags = create ? KEY_LOOKUP_CREATE : 0; +	key_ref = lookup_user_key(id, lflags, KEY_SEARCH);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error; @@ -309,7 +312,7 @@ long keyctl_update_key(key_serial_t id,  	}  	/* find the target key (which must be writable) */ -	key_ref = lookup_user_key(id, 0, 0, KEY_WRITE); +	key_ref = lookup_user_key(id, 0, KEY_WRITE);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error2; @@ -337,10 +340,16 @@ long keyctl_revoke_key(key_serial_t id)  	key_ref_t key_ref;  	long ret; -	key_ref = lookup_user_key(id, 0, 0, KEY_WRITE); +	key_ref = lookup_user_key(id, 0, KEY_WRITE);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref); -		goto error; +		if (ret != -EACCES) +			goto error; +		key_ref = lookup_user_key(id, 0, KEY_SETATTR); +		if (IS_ERR(key_ref)) { +			ret = PTR_ERR(key_ref); +			goto error; +		}  	}  	key_revoke(key_ref_to_ptr(key_ref)); @@ -363,7 +372,7 @@ long keyctl_keyring_clear(key_serial_t ringid)  	key_ref_t keyring_ref;  	long ret; -	keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE); +	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref);  		goto error; @@ -389,13 +398,13 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)  	key_ref_t keyring_ref, key_ref;  	long ret; -	keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE); +	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref);  		goto error;  	} -	key_ref = lookup_user_key(id, 1, 0, KEY_LINK); +	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_LINK);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error2; @@ -423,13 +432,13 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)  	key_ref_t keyring_ref, key_ref;  	long ret; -	keyring_ref = lookup_user_key(ringid, 0, 0, KEY_WRITE); +	keyring_ref = lookup_user_key(ringid, 0, KEY_WRITE);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref);  		goto error;  	} -	key_ref = lookup_user_key(id, 0, 0, 0); +	key_ref = lookup_user_key(id, KEY_LOOKUP_FOR_UNLINK, 0);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error2; @@ -465,7 +474,7 @@ long keyctl_describe_key(key_serial_t keyid,  	char *tmpbuf;  	long ret; -	key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW); +	key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW);  	if (IS_ERR(key_ref)) {  		/* viewing a key under construction is permitted if we have the  		 * authorisation token handy */ @@ -474,7 +483,8 @@ long keyctl_describe_key(key_serial_t keyid,  			if (!IS_ERR(instkey)) {  				key_put(instkey);  				key_ref = lookup_user_key(keyid, -							  0, 1, 0); +							  KEY_LOOKUP_PARTIAL, +							  0);  				if (!IS_ERR(key_ref))  					goto okay;  			} @@ -558,7 +568,7 @@ long keyctl_keyring_search(key_serial_t ringid,  	}  	/* get the keyring at which to begin the search */ -	keyring_ref = lookup_user_key(ringid, 0, 0, KEY_SEARCH); +	keyring_ref = lookup_user_key(ringid, 0, KEY_SEARCH);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref);  		goto error2; @@ -567,7 +577,8 @@ long keyctl_keyring_search(key_serial_t ringid,  	/* get the destination keyring if specified */  	dest_ref = NULL;  	if (destringid) { -		dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE); +		dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE, +					   KEY_WRITE);  		if (IS_ERR(dest_ref)) {  			ret = PTR_ERR(dest_ref);  			goto error3; @@ -637,7 +648,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)  	long ret;  	/* find the key first */ -	key_ref = lookup_user_key(keyid, 0, 0, 0); +	key_ref = lookup_user_key(keyid, 0, 0);  	if (IS_ERR(key_ref)) {  		ret = -ENOKEY;  		goto error; @@ -700,7 +711,8 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)  	if (uid == (uid_t) -1 && gid == (gid_t) -1)  		goto error; -	key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR); +	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, +				  KEY_SETATTR);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error; @@ -805,7 +817,8 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)  	if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))  		goto error; -	key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR); +	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, +				  KEY_SETATTR);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error; @@ -847,7 +860,7 @@ static long get_instantiation_keyring(key_serial_t ringid,  	/* if a specific keyring is nominated by ID, then use that */  	if (ringid > 0) { -		dkref = lookup_user_key(ringid, 1, 0, KEY_WRITE); +		dkref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);  		if (IS_ERR(dkref))  			return PTR_ERR(dkref);  		*_dest_keyring = key_ref_to_ptr(dkref); @@ -1083,7 +1096,8 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)  	time_t expiry;  	long ret; -	key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR); +	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, +				  KEY_SETATTR);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error; @@ -1101,6 +1115,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)  	}  	key->expiry = expiry; +	key_schedule_gc(key->expiry);  	up_write(&key->sem);  	key_put(key); @@ -1170,7 +1185,7 @@ long keyctl_get_security(key_serial_t keyid,  	char *context;  	long ret; -	key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW); +	key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW);  	if (IS_ERR(key_ref)) {  		if (PTR_ERR(key_ref) != -EACCES)  			return PTR_ERR(key_ref); @@ -1182,7 +1197,7 @@ long keyctl_get_security(key_serial_t keyid,  			return PTR_ERR(key_ref);  		key_put(instkey); -		key_ref = lookup_user_key(keyid, 0, 1, 0); +		key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, 0);  		if (IS_ERR(key_ref))  			return PTR_ERR(key_ref);  	} @@ -1213,6 +1228,105 @@ long keyctl_get_security(key_serial_t keyid,  	return ret;  } +/* + * attempt to install the calling process's session keyring on the process's + * parent process + * - the keyring must exist and must grant us LINK permission + * - implements keyctl(KEYCTL_SESSION_TO_PARENT) + */ +long keyctl_session_to_parent(void) +{ +	struct task_struct *me, *parent; +	const struct cred *mycred, *pcred; +	struct cred *cred, *oldcred; +	key_ref_t keyring_r; +	int ret; + +	keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK); +	if (IS_ERR(keyring_r)) +		return PTR_ERR(keyring_r); + +	/* our parent is going to need a new cred struct, a new tgcred struct +	 * and new security data, so we allocate them here to prevent ENOMEM in +	 * our parent */ +	ret = -ENOMEM; +	cred = cred_alloc_blank(); +	if (!cred) +		goto error_keyring; + +	cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r); +	keyring_r = NULL; + +	me = current; +	write_lock_irq(&tasklist_lock); + +	parent = me->real_parent; +	ret = -EPERM; + +	/* the parent mustn't be init and mustn't be a kernel thread */ +	if (parent->pid <= 1 || !parent->mm) +		goto not_permitted; + +	/* the parent must be single threaded */ +	if (atomic_read(&parent->signal->count) != 1) +		goto not_permitted; + +	/* the parent and the child must have different session keyrings or +	 * there's no point */ +	mycred = current_cred(); +	pcred = __task_cred(parent); +	if (mycred == pcred || +	    mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) +		goto already_same; + +	/* the parent must have the same effective ownership and mustn't be +	 * SUID/SGID */ +	if (pcred-> uid	!= mycred->euid	|| +	    pcred->euid	!= mycred->euid	|| +	    pcred->suid	!= mycred->euid	|| +	    pcred-> gid	!= mycred->egid	|| +	    pcred->egid	!= mycred->egid	|| +	    pcred->sgid	!= mycred->egid) +		goto not_permitted; + +	/* the keyrings must have the same UID */ +	if (pcred ->tgcred->session_keyring->uid != mycred->euid || +	    mycred->tgcred->session_keyring->uid != mycred->euid) +		goto not_permitted; + +	/* the LSM must permit the replacement of the parent's keyring with the +	 * keyring from this process */ +	ret = security_key_session_to_parent(mycred, pcred, +					     key_ref_to_ptr(keyring_r)); +	if (ret < 0) +		goto not_permitted; + +	/* if there's an already pending keyring replacement, then we replace +	 * that */ +	oldcred = parent->replacement_session_keyring; + +	/* the replacement session keyring is applied just prior to userspace +	 * restarting */ +	parent->replacement_session_keyring = cred; +	cred = NULL; +	set_ti_thread_flag(task_thread_info(parent), TIF_NOTIFY_RESUME); + +	write_unlock_irq(&tasklist_lock); +	if (oldcred) +		put_cred(oldcred); +	return 0; + +already_same: +	ret = 0; +not_permitted: +	put_cred(cred); +	return ret; + +error_keyring: +	key_ref_put(keyring_r); +	return ret; +} +  /*****************************************************************************/  /*   * the key control system call @@ -1298,6 +1412,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,  					   (char __user *) arg3,  					   (size_t) arg4); +	case KEYCTL_SESSION_TO_PARENT: +		return keyctl_session_to_parent(); +  	default:  		return -EOPNOTSUPP;  	}  |