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 *)≺ |
|
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 } |