usr/src/uts/common/os/klpd.c
changeset 12273 63678502e95e
parent 6997 056043f166c6
equal deleted inserted replaced
12272:400aca678a81 12273:63678502e95e
    18  *
    18  *
    19  * CDDL HEADER END
    19  * CDDL HEADER END
    20  */
    20  */
    21 
    21 
    22 /*
    22 /*
    23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
    23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
    24  * Use is subject to license terms.
       
    25  */
    24  */
    26 
       
    27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
       
    28 
    25 
    29 #include <sys/atomic.h>
    26 #include <sys/atomic.h>
    30 #include <sys/door.h>
    27 #include <sys/door.h>
    31 #include <sys/proc.h>
    28 #include <sys/proc.h>
    32 #include <sys/cred_impl.h>
    29 #include <sys/cred_impl.h>
   137 		p->klpd_next->klpd_refp = p->klpd_refp;
   134 		p->klpd_next->klpd_refp = p->klpd_refp;
   138 	p->klpd_refp = NULL;
   135 	p->klpd_refp = NULL;
   139 }
   136 }
   140 
   137 
   141 /*
   138 /*
   142  * Remove the head of the klpd list and decrement its refcnt.
   139  * Remove all elements of the klpd list and decrement their refcnts.
   143  * The lock guarding the list should be held; this function is
   140  * The lock guarding the list should be held; this function is
   144  * called when we are sure we want to remove the entry from the
   141  * called when we are sure we want to destroy the list completely
   145  * list but not so sure that the reference count has dropped back to
   142  * list but not so sure that the reference counts of all elements have
   146  * 1 and is specifically intended to remove the non-list variants.
   143  * dropped back to 1.
   147  */
   144  */
   148 void
   145 void
   149 klpd_remove(klpd_reg_t **pp)
   146 klpd_freelist(klpd_reg_t **pp)
   150 {
   147 {
   151 	klpd_reg_t *p = *pp;
   148 	klpd_reg_t *p;
   152 	if (p == NULL)
   149 
   153 		return;
   150 	while ((p = *pp) != NULL) {
   154 	ASSERT(p->klpd_next == NULL);
   151 		klpd_unlink(p);
   155 	klpd_unlink(p);
   152 		klpd_rele(p);
   156 	klpd_rele(p);
   153 	}
   157 }
   154 }
   158 
   155 
   159 /*
   156 /*
   160  * Link new entry in list.  The Boolean argument specifies whether this
   157  * Link new entry in list.  The Boolean argument specifies whether this
   161  * list can contain only a single item or multiple items.
   158  * list can contain only a single item or multiple items.
   190  * for now, it's just one bit.
   187  * for now, it's just one bit.
   191  */
   188  */
   192 static klpd_head_t *
   189 static klpd_head_t *
   193 klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
   190 klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
   194 {
   191 {
   195 	char	*comp;
   192 	char	*tmp;
   196 	uint_t	type;
   193 	uint_t	type;
   197 	vnode_t *vp;
   194 	vnode_t *vp;
   198 	size_t	len = sizeof (priv_set_t) + sizeof (klpd_head_t);
   195 	size_t	len = sizeof (priv_set_t) + sizeof (klpd_head_t);
   199 	size_t	plen, clen;
   196 	size_t	plen, clen;
   200 	int	proto;
   197 	int	proto;
   212 		len += offsetof(klpd_arg_t, kla_str);
   209 		len += offsetof(klpd_arg_t, kla_str);
   213 		vp = va_arg(ap, vnode_t *);
   210 		vp = va_arg(ap, vnode_t *);
   214 		if (vp == NULL)
   211 		if (vp == NULL)
   215 			return (NULL);
   212 			return (NULL);
   216 
   213 
   217 		comp = va_arg(ap, char *);
   214 		tmp = va_arg(ap, char *);
   218 
   215 
   219 		if (comp != NULL && *comp != '\0')
   216 		if (tmp != NULL && *tmp != '\0')
   220 			clen = strlen(comp) + 1;
   217 			clen = strlen(tmp) + 1;
   221 		else
   218 		else
   222 			clen = 0;
   219 			clen = 0;
   223 
   220 
   224 		len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
   221 		len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
   225 		khp = kmem_zalloc(len, KM_SLEEP);
   222 		khp = kmem_zalloc(len, KM_SLEEP);
   240 			}
   237 			}
   241 			/* Don't make root into a double "/" */
   238 			/* Don't make root into a double "/" */
   242 			if (plen <= 2)
   239 			if (plen <= 2)
   243 				plen = 0;
   240 				plen = 0;
   244 			kap->kla_str[plen] = '/';
   241 			kap->kla_str[plen] = '/';
   245 			bcopy(comp, &kap->kla_str[plen + 1], clen);
   242 			bcopy(tmp, &kap->kla_str[plen + 1], clen);
   246 		}
   243 		}
   247 		break;
   244 		break;
   248 	case KLPDARG_PORT:
   245 	case KLPDARG_PORT:
   249 		proto = va_arg(ap, int);
   246 		proto = va_arg(ap, int);
   250 		switch (proto) {
   247 		switch (proto) {
   634 	if (kpp != NULL) {
   631 	if (kpp != NULL) {
   635 		mutex_enter(&klpd_mutex);
   632 		mutex_enter(&klpd_mutex);
   636 		if (kpp->kpj_klpd == NULL)
   633 		if (kpp->kpj_klpd == NULL)
   637 			res = ESRCH;
   634 			res = ESRCH;
   638 		else
   635 		else
   639 			klpd_remove(&kpp->kpj_klpd);
   636 			klpd_freelist(&kpp->kpj_klpd);
   640 		mutex_exit(&klpd_mutex);
   637 		mutex_exit(&klpd_mutex);
   641 		project_rele(kpp);
   638 		project_rele(kpp);
   642 		goto out;
   639 		goto out;
   643 	} else if ((int)pid > 0) {
   640 	} else if ((int)pid > 0) {
   644 		mutex_enter(&pidlock);
   641 		mutex_enter(&pidlock);
   719 	mutex_exit(&crk->crkl_lock);
   716 	mutex_exit(&crk->crkl_lock);
   720 
   717 
   721 	if (old != NULL)
   718 	if (old != NULL)
   722 		klpd_rele(old);
   719 		klpd_rele(old);
   723 }
   720 }
       
   721 
       
   722 /* Allocate and register the pfexec specific callback */
       
   723 int
       
   724 pfexec_reg(int did)
       
   725 {
       
   726 	door_handle_t dh;
       
   727 	int err = secpolicy_pfexec_register(CRED());
       
   728 	klpd_reg_t *pfx;
       
   729 	door_info_t di;
       
   730 	zone_t *myzone = crgetzone(CRED());
       
   731 
       
   732 	if (err != 0)
       
   733 		return (set_errno(err));
       
   734 
       
   735 	dh = door_ki_lookup(did);
       
   736 	if (dh == NULL || door_ki_info(dh, &di) != 0)
       
   737 		return (set_errno(EBADF));
       
   738 
       
   739 	pfx = kmem_zalloc(sizeof (*pfx), KM_SLEEP);
       
   740 
       
   741 	pfx->klpd_door = dh;
       
   742 	pfx->klpd_door_pid = di.di_target;
       
   743 	pfx->klpd_ref = 1;
       
   744 	pfx->klpd_cred = NULL;
       
   745 	mutex_enter(&myzone->zone_lock);
       
   746 	pfx = klpd_link(pfx, &myzone->zone_pfexecd, B_TRUE);
       
   747 	mutex_exit(&myzone->zone_lock);
       
   748 	if (pfx != NULL)
       
   749 		klpd_rele(pfx);
       
   750 
       
   751 	return (0);
       
   752 }
       
   753 
       
   754 int
       
   755 pfexec_unreg(int did)
       
   756 {
       
   757 	door_handle_t dh;
       
   758 	int err = 0;
       
   759 	zone_t *myzone = crgetzone(CRED());
       
   760 	klpd_reg_t *pfd;
       
   761 
       
   762 	dh = door_ki_lookup(did);
       
   763 	if (dh == NULL)
       
   764 		return (set_errno(EBADF));
       
   765 
       
   766 	mutex_enter(&myzone->zone_lock);
       
   767 	pfd = myzone->zone_pfexecd;
       
   768 	if (pfd != NULL && pfd->klpd_door == dh) {
       
   769 		klpd_unlink(pfd);
       
   770 	} else {
       
   771 		pfd = NULL;
       
   772 		err = EINVAL;
       
   773 	}
       
   774 	mutex_exit(&myzone->zone_lock);
       
   775 	door_ki_rele(dh);
       
   776 	/*
       
   777 	 * crfree() cannot be called with zone_lock held; it is called
       
   778 	 * indirectly through closing the door handle
       
   779 	 */
       
   780 	if (pfd != NULL)
       
   781 		klpd_rele(pfd);
       
   782 	if (err != 0)
       
   783 		return (set_errno(err));
       
   784 	return (0);
       
   785 }
       
   786 
       
   787 static int
       
   788 get_path(char *buf, const char *path, int len)
       
   789 {
       
   790 	size_t lc;
       
   791 	char *s;
       
   792 
       
   793 	if (len < 0)
       
   794 		len = strlen(path);
       
   795 
       
   796 	if (*path == '/' && len < MAXPATHLEN) {
       
   797 		(void) strcpy(buf, path);
       
   798 		return (0);
       
   799 	}
       
   800 	/*
       
   801 	 * Build the pathname using the current directory + resolve pathname.
       
   802 	 * The resolve pathname either starts with a normal component and
       
   803 	 * we can just concatenate them or it starts with one
       
   804 	 * or more ".." component and we can remove those; the
       
   805 	 * last one cannot be a ".." and the current directory has
       
   806 	 * more components than the number of ".." in the resolved pathname.
       
   807 	 */
       
   808 	if (dogetcwd(buf, MAXPATHLEN) != 0)
       
   809 		return (-1);
       
   810 
       
   811 	lc = strlen(buf);
       
   812 
       
   813 	while (len > 3 && strncmp("../", path, 3) == 0) {
       
   814 		len -= 3;
       
   815 		path += 3;
       
   816 
       
   817 		s = strrchr(buf, '/');
       
   818 		if (s == NULL || s == buf)
       
   819 			return (-1);
       
   820 
       
   821 		*s = '\0';
       
   822 		lc = s - buf;
       
   823 	}
       
   824 	/* Add a "/" and a NUL */
       
   825 	if (lc < 2 || lc + len + 2 >= MAXPATHLEN)
       
   826 		return (-1);
       
   827 
       
   828 	buf[lc] = '/';
       
   829 	(void) strcpy(buf + lc + 1, path);
       
   830 
       
   831 	return (0);
       
   832 }
       
   833 
       
   834 /*
       
   835  * Perform the pfexec upcall.
       
   836  *
       
   837  * The pfexec upcall is different from the klpd_upcall in that a failure
       
   838  * will lead to a denial of execution.
       
   839  */
       
   840 int
       
   841 pfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr,
       
   842     boolean_t *scrub)
       
   843 {
       
   844 	klpd_reg_t *pfd;
       
   845 	pfexec_arg_t *pap;
       
   846 	pfexec_reply_t pr, *prp;
       
   847 	door_arg_t da;
       
   848 	int dres;
       
   849 	cred_t *ncr = NULL;
       
   850 	int err = -1;
       
   851 	priv_set_t *iset;
       
   852 	priv_set_t *lset;
       
   853 	zone_t *myzone = crgetzone(CRED());
       
   854 	size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
       
   855 
       
   856 	/* Find registration */
       
   857 	mutex_enter(&myzone->zone_lock);
       
   858 	if ((pfd = myzone->zone_pfexecd) != NULL)
       
   859 		klpd_hold(pfd);
       
   860 	mutex_exit(&myzone->zone_lock);
       
   861 
       
   862 	if (pfd == NULL)
       
   863 		return (0);
       
   864 
       
   865 	if (pfd->klpd_door_pid == curproc->p_pid) {
       
   866 		klpd_rele(pfd);
       
   867 		return (0);
       
   868 	}
       
   869 
       
   870 	pap = kmem_zalloc(pasize, KM_SLEEP);
       
   871 
       
   872 	if (get_path(pap->pfa_path, rpnp->pn_path, rpnp->pn_pathlen) == -1)
       
   873 		goto out1;
       
   874 
       
   875 	pap->pfa_vers = PFEXEC_ARG_VERS;
       
   876 	pap->pfa_call = PFEXEC_EXEC_ATTRS;
       
   877 	pap->pfa_len = pasize;
       
   878 	pap->pfa_uid = crgetruid(cr);
       
   879 
       
   880 	da.data_ptr = (char *)pap;
       
   881 	da.data_size = pap->pfa_len;
       
   882 	da.desc_ptr = NULL;
       
   883 	da.desc_num = 0;
       
   884 	da.rbuf = (char *)&pr;
       
   885 	da.rsize = sizeof (pr);
       
   886 
       
   887 	while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
       
   888 		switch (dres) {
       
   889 		case EAGAIN:
       
   890 			delay(1);
       
   891 			continue;
       
   892 		case EINVAL:
       
   893 		case EBADF:
       
   894 			/* FALLTHROUGH */
       
   895 		case EINTR:
       
   896 			/* FALLTHROUGH */
       
   897 		default:
       
   898 			goto out;
       
   899 		}
       
   900 	}
       
   901 
       
   902 	prp = (pfexec_reply_t *)da.rbuf;
       
   903 	/*
       
   904 	 * Check the size of the result and the alignment of the
       
   905 	 * privilege sets.
       
   906 	 */
       
   907 	if (da.rsize < sizeof (pr) ||
       
   908 	    prp->pfr_ioff > da.rsize - sizeof (priv_set_t) ||
       
   909 	    prp->pfr_loff > da.rsize - sizeof (priv_set_t) ||
       
   910 	    (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0 ||
       
   911 	    (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0)
       
   912 		goto out;
       
   913 
       
   914 	/*
       
   915 	 * Get results:
       
   916 	 *	allow/allow with additional credentials/disallow[*]
       
   917 	 *
       
   918 	 *	euid, uid, egid, gid, privs, and limitprivs
       
   919 	 * We now have somewhat more flexibility we could even set E and P
       
   920 	 * judiciously but that would break some currently valid assumptions
       
   921 	 *	[*] Disallow is not readily supported by always including
       
   922 	 *	the Basic Solaris User profile in all user's profiles.
       
   923 	 */
       
   924 
       
   925 	if (!prp->pfr_allowed) {
       
   926 		err = EACCES;
       
   927 		goto out;
       
   928 	}
       
   929 	if (!prp->pfr_setcred) {
       
   930 		err = 0;
       
   931 		goto out;
       
   932 	}
       
   933 	ncr = crdup((cred_t *)cr);
       
   934 
       
   935 	/*
       
   936 	 * Generate the new credential set scrubenv if ruid != euid (or set)
       
   937 	 * the "I'm set-uid flag" but that is not inherited so scrubbing
       
   938 	 * the environment is a requirement.
       
   939 	 */
       
   940 	/* Set uids or gids, note that -1 will do the right thing */
       
   941 	if (crsetresuid(ncr, prp->pfr_ruid, prp->pfr_euid, prp->pfr_euid) != 0)
       
   942 		goto out;
       
   943 	if (crsetresgid(ncr, prp->pfr_rgid, prp->pfr_egid, prp->pfr_egid) != 0)
       
   944 		goto out;
       
   945 
       
   946 	*scrub = prp->pfr_scrubenv;
       
   947 
       
   948 	if (prp->pfr_clearflag)
       
   949 		CR_FLAGS(ncr) &= ~PRIV_PFEXEC;
       
   950 
       
   951 	/* We cannot exceed our Limit set, no matter what */
       
   952 	iset = PFEXEC_REPLY_IPRIV(prp);
       
   953 
       
   954 	if (iset != NULL) {
       
   955 		if (!priv_issubset(iset, &CR_LPRIV(ncr)))
       
   956 			goto out;
       
   957 		priv_union(iset, &CR_IPRIV(ncr));
       
   958 	}
       
   959 
       
   960 	/* Nor can we increate our Limit set itself */
       
   961 	lset = PFEXEC_REPLY_LPRIV(prp);
       
   962 
       
   963 	if (lset != NULL) {
       
   964 		if (!priv_issubset(lset, &CR_LPRIV(ncr)))
       
   965 			goto out;
       
   966 		CR_LPRIV(ncr) = *lset;
       
   967 	}
       
   968 
       
   969 	/* Exec will do the standard set operations */
       
   970 
       
   971 	err = 0;
       
   972 out:
       
   973 	if (da.rbuf != (char *)&pr)
       
   974 		kmem_free(da.rbuf, da.rsize);
       
   975 out1:
       
   976 	kmem_free(pap, pasize);
       
   977 	klpd_rele(pfd);
       
   978 	if (ncr != NULL) {
       
   979 		if (err == 0)
       
   980 			*pfcr = ncr;
       
   981 		else
       
   982 			crfree(ncr);
       
   983 	}
       
   984 	return (err);
       
   985 }
       
   986 
       
   987 int
       
   988 get_forced_privs(const cred_t *cr, const char *respn, priv_set_t *set)
       
   989 {
       
   990 	klpd_reg_t *pfd;
       
   991 	pfexec_arg_t *pap;
       
   992 	door_arg_t da;
       
   993 	int dres;
       
   994 	int err = -1;
       
   995 	priv_set_t *fset, pmem;
       
   996 	cred_t *zkcr;
       
   997 	zone_t *myzone = crgetzone(cr);
       
   998 	size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
       
   999 
       
  1000 	mutex_enter(&myzone->zone_lock);
       
  1001 	if ((pfd = myzone->zone_pfexecd) != NULL)
       
  1002 		klpd_hold(pfd);
       
  1003 	mutex_exit(&myzone->zone_lock);
       
  1004 
       
  1005 	if (pfd == NULL)
       
  1006 		return (-1);
       
  1007 
       
  1008 	if (pfd->klpd_door_pid == curproc->p_pid) {
       
  1009 		klpd_rele(pfd);
       
  1010 		return (0);
       
  1011 	}
       
  1012 
       
  1013 	pap = kmem_zalloc(pasize, KM_SLEEP);
       
  1014 
       
  1015 	if (get_path(pap->pfa_path, respn, -1) == -1)
       
  1016 		goto out1;
       
  1017 
       
  1018 	pap->pfa_vers = PFEXEC_ARG_VERS;
       
  1019 	pap->pfa_call = PFEXEC_FORCED_PRIVS;
       
  1020 	pap->pfa_len = pasize;
       
  1021 	pap->pfa_uid = (uid_t)-1;			/* Not relevant */
       
  1022 
       
  1023 	da.data_ptr = (char *)pap;
       
  1024 	da.data_size = pap->pfa_len;
       
  1025 	da.desc_ptr = NULL;
       
  1026 	da.desc_num = 0;
       
  1027 	da.rbuf = (char *)&pmem;
       
  1028 	da.rsize = sizeof (pmem);
       
  1029 
       
  1030 	while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
       
  1031 		switch (dres) {
       
  1032 		case EAGAIN:
       
  1033 			delay(1);
       
  1034 			continue;
       
  1035 		case EINVAL:
       
  1036 		case EBADF:
       
  1037 		case EINTR:
       
  1038 		default:
       
  1039 			goto out;
       
  1040 		}
       
  1041 	}
       
  1042 
       
  1043 	/*
       
  1044 	 * Check the size of the result, it's a privilege set.
       
  1045 	 */
       
  1046 	if (da.rsize != sizeof (priv_set_t))
       
  1047 		goto out;
       
  1048 
       
  1049 	fset = (priv_set_t *)da.rbuf;
       
  1050 
       
  1051 	/*
       
  1052 	 * We restrict the forced privileges with whatever is available in
       
  1053 	 * the current zone.
       
  1054 	 */
       
  1055 	zkcr = zone_kcred();
       
  1056 	priv_intersect(&CR_LPRIV(zkcr), fset);
       
  1057 
       
  1058 	/*
       
  1059 	 * But we fail if the forced privileges are not found in the current
       
  1060 	 * Limit set.
       
  1061 	 */
       
  1062 	if (!priv_issubset(fset, &CR_LPRIV(cr))) {
       
  1063 		err = EACCES;
       
  1064 	} else if (!priv_isemptyset(fset)) {
       
  1065 		err = 0;
       
  1066 		*set = *fset;
       
  1067 	}
       
  1068 out:
       
  1069 	if (da.rbuf != (char *)&pmem)
       
  1070 		kmem_free(da.rbuf, da.rsize);
       
  1071 out1:
       
  1072 	kmem_free(pap, pasize);
       
  1073 	klpd_rele(pfd);
       
  1074 	return (err);
       
  1075 }
       
  1076 
       
  1077 int
       
  1078 check_user_privs(const cred_t *cr, const priv_set_t *set)
       
  1079 {
       
  1080 	klpd_reg_t *pfd;
       
  1081 	pfexec_arg_t *pap;
       
  1082 	door_arg_t da;
       
  1083 	int dres;
       
  1084 	int err = -1;
       
  1085 	zone_t *myzone = crgetzone(cr);
       
  1086 	size_t pasize = PFEXEC_ARG_SIZE(sizeof (priv_set_t));
       
  1087 	uint32_t res;
       
  1088 
       
  1089 	mutex_enter(&myzone->zone_lock);
       
  1090 	if ((pfd = myzone->zone_pfexecd) != NULL)
       
  1091 		klpd_hold(pfd);
       
  1092 	mutex_exit(&myzone->zone_lock);
       
  1093 
       
  1094 	if (pfd == NULL)
       
  1095 		return (-1);
       
  1096 
       
  1097 	if (pfd->klpd_door_pid == curproc->p_pid) {
       
  1098 		klpd_rele(pfd);
       
  1099 		return (0);
       
  1100 	}
       
  1101 
       
  1102 	pap = kmem_zalloc(pasize, KM_SLEEP);
       
  1103 
       
  1104 	*(priv_set_t *)&pap->pfa_buf = *set;
       
  1105 
       
  1106 	pap->pfa_vers = PFEXEC_ARG_VERS;
       
  1107 	pap->pfa_call = PFEXEC_USER_PRIVS;
       
  1108 	pap->pfa_len = pasize;
       
  1109 	pap->pfa_uid = crgetruid(cr);
       
  1110 
       
  1111 	da.data_ptr = (char *)pap;
       
  1112 	da.data_size = pap->pfa_len;
       
  1113 	da.desc_ptr = NULL;
       
  1114 	da.desc_num = 0;
       
  1115 	da.rbuf = (char *)&res;
       
  1116 	da.rsize = sizeof (res);
       
  1117 
       
  1118 	while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
       
  1119 		switch (dres) {
       
  1120 		case EAGAIN:
       
  1121 			delay(1);
       
  1122 			continue;
       
  1123 		case EINVAL:
       
  1124 		case EBADF:
       
  1125 		case EINTR:
       
  1126 		default:
       
  1127 			goto out;
       
  1128 		}
       
  1129 	}
       
  1130 
       
  1131 	/*
       
  1132 	 * Check the size of the result.
       
  1133 	 */
       
  1134 	if (da.rsize != sizeof (res))
       
  1135 		goto out;
       
  1136 
       
  1137 	if (*(uint32_t *)da.rbuf == 1)
       
  1138 		err = 0;
       
  1139 out:
       
  1140 	if (da.rbuf != (char *)&res)
       
  1141 		kmem_free(da.rbuf, da.rsize);
       
  1142 out1:
       
  1143 	kmem_free(pap, pasize);
       
  1144 	klpd_rele(pfd);
       
  1145 	return (err);
       
  1146 }