1694 | | a = PyString_FromStringAndSize(argv0, n); |
1695 | | if (a == NULL) |
1696 | | Py_FatalError("no mem for sys.path insertion"); |
1697 | | if (PyList_Insert(path, 0, a) < 0) |
1698 | | Py_FatalError("sys.path.insert(0) failed"); |
1699 | | Py_DECREF(a); |
| 1691 | } |
| 1692 | |
| 1693 | /* Copy n bytes of arg to parent (the parent directory |
| 1694 | * to be added to sys.path) */ |
| 1695 | char parent[MAXPATHLEN+1]; |
| 1696 | memcpy(parent, arg, n); |
| 1697 | parent[n] = '\0'; |
| 1698 | |
| 1699 | /* Do some security checks before adding "parent" to sys.path */ |
| 1700 | #ifdef HAVE_STAT |
| 1701 | struct stat parent_stat; |
| 1702 | struct stat arg_stat; |
| 1703 | struct stat program_stat; /* Python program */ |
| 1704 | char warnmsg[MAXPATHLEN + 400]; |
| 1705 | const char *lecture = "Untrusted users could put files in this " |
| 1706 | "directory which might then be imported by your Python code. " |
| 1707 | "As a general precaution from similar exploits, " |
| 1708 | "you should not execute Python code from this directory"; |
| 1709 | if (stat( (parent[0] != '\0') ? parent : ".", &parent_stat) != 0) { |
| 1710 | snprintf(warnmsg, sizeof(warnmsg), "not adding '%s' to sys.path since its status cannot be determined", parent); |
| 1711 | return PyErr_WarnEx(PyExc_RuntimeWarning, warnmsg, 1); |
| 1712 | } |
| 1713 | if (!S_ISDIR(parent_stat.st_mode)) { |
| 1714 | snprintf(warnmsg, sizeof(warnmsg), "not adding '%s' to sys.path since it's not a directory", parent); |
| 1715 | return PyErr_WarnEx(PyExc_RuntimeWarning, warnmsg, 1); |
| 1716 | } |
| 1717 | |
| 1718 | if (given_arg[0] != '\0' && stat(given_arg, &arg_stat) == 0) { |
| 1719 | /* If parent does *not* have the sticky bit set, "arg" is at |
| 1720 | * least as writable as "parent". This obviously only applies |
| 1721 | * if "arg" is an existing file/directory inside "parent", which |
| 1722 | * is the case here. */ |
| 1723 | if (!(parent_stat.st_mode & S_ISVTX)) |
| 1724 | arg_stat.st_mode |= parent_stat.st_mode; |
| 1725 | /* Only keep group bits if the group is the same as the |
| 1726 | * group of "parent" (otherwise the group is considered unsafe). */ |
| 1727 | if (arg_stat.st_gid != parent_stat.st_gid) |
| 1728 | arg_stat.st_mode &= 0707; |
| 1729 | } else { |
| 1730 | /* given_arg was "" or stat() failed, manually set relevant |
| 1731 | * stat members to safe values. */ |
| 1732 | arg_stat.st_mode = 0644; |
| 1733 | arg_stat.st_uid = 0; |
| 1734 | } |
| 1735 | |
| 1736 | if (stat(Py_GetProgramFullPath(), &program_stat) == 0) { |
| 1737 | /* Only keep group bits if the group is the same as the |
| 1738 | * group of "parent" (otherwise the group is considered unsafe). */ |
| 1739 | if (program_stat.st_gid != parent_stat.st_gid) |
| 1740 | program_stat.st_mode &= 0707; |
| 1741 | } else { |
| 1742 | /* stat() failed, set relevant stat members to safe values. */ |
| 1743 | program_stat.st_mode = 0644; |
| 1744 | program_stat.st_uid = 0; |
| 1745 | } |
| 1746 | |
| 1747 | /* Check permissions, check that the "parent" directory is not |
| 1748 | * more permissive than the script "arg" or the Python program |
| 1749 | * "program". Otherwise adding "parent" to sys.path is a security |
| 1750 | * risk. */ |
| 1751 | if (parent_stat.st_mode & 0002) { |
| 1752 | /* (A) "parent" is world-writable */ |
| 1753 | if ((arg_stat.st_mode & 0002) == 0 && (program_stat.st_mode & 0002) == 0) { |
| 1754 | snprintf(warnmsg, sizeof(warnmsg), |
| 1755 | "not adding directory '%s' to sys.path since everybody can write to it.\n%s", |
| 1756 | parent, lecture); |
| 1757 | return PyErr_WarnEx(PyExc_RuntimeWarning, warnmsg, 1); |
| 1758 | } |
| 1759 | } else if (parent_stat.st_mode & 0020) { |
| 1760 | /* (B) "parent" is group-writable. Recall that the group |
| 1761 | * permissions of "arg" and "program" refer to the group owning |
| 1762 | * "parent". */ |
| 1763 | if ((arg_stat.st_mode & 0022) == 0 && (program_stat.st_mode & 0022) == 0) { |
| 1764 | snprintf(warnmsg, sizeof(warnmsg), |
| 1765 | "not adding directory '%s' to sys.path since it's writable by an untrusted group.\n%s", |
| 1766 | parent, lecture); |
| 1767 | return PyErr_WarnEx(PyExc_RuntimeWarning, warnmsg, 1); |
| 1768 | } |
| 1769 | } else { |
| 1770 | /* (C) parent is neither group-, neither world-writable. |
| 1771 | * We are safe if "arg" or "program" is group- or |
| 1772 | * world-writable or if "parent" is owned by a trusted user: |
| 1773 | * either the same owner as "arg" or "program", |
| 1774 | * or root, or the current user. */ |
| 1775 | if ( |
| 1776 | (arg_stat.st_mode & 0022) == 0 && |
| 1777 | (program_stat.st_mode & 0022) == 0 && |
| 1778 | parent_stat.st_uid != arg_stat.st_uid && |
| 1779 | parent_stat.st_uid != program_stat.st_uid && |
| 1780 | parent_stat.st_uid != 0 && |
| 1781 | parent_stat.st_uid != getuid()) { |
| 1782 | snprintf(warnmsg, sizeof(warnmsg), |
| 1783 | "not adding directory '%s' to sys.path since it's not owned by a trusted user.\n%s", |
| 1784 | parent, lecture); |
| 1785 | return PyErr_WarnEx(PyExc_RuntimeWarning, warnmsg, 1); |
| 1786 | } |
| 1787 | } |
| 1788 | #endif /* HAVE_STAT */ |
| 1789 | |
| 1790 | PyObject *a = PyString_FromString(parent); |
| 1791 | if (a == NULL) |
| 1792 | Py_FatalError("no mem for sys.path insertion"); |
| 1793 | if (PyList_Insert(path, 0, a) < 0) |
| 1794 | return -1; |
| 1795 | Py_DECREF(a); |
| 1796 | |
| 1797 | return 0; |
| 1798 | } |
| 1799 | |
| 1800 | void |
| 1801 | PySys_SetArgvEx(int argc, char **argv, int updatepath) |
| 1802 | { |
| 1803 | PyObject *av = makeargvobject(argc, argv); |
| 1804 | PyObject *path = PySys_GetObject("path"); |
| 1805 | if (av == NULL) |
| 1806 | Py_FatalError("no mem for sys.argv"); |
| 1807 | if (PySys_SetObject("argv", av) != 0) |
| 1808 | Py_FatalError("can't assign sys.argv"); |
| 1809 | |
| 1810 | if (updatepath && path != NULL) { |
| 1811 | char *argv0; |
| 1812 | if (argc <= 0 || argv[0] == NULL || strcmp(argv[0], "-c") == 0) { |
| 1813 | /* If there is no argv[0] or argv[0] equals "-c", add "" to sys.path */ |
| 1814 | argv0 = ""; |
| 1815 | } |
| 1816 | else { |
| 1817 | argv0 = argv[0]; |
| 1818 | } |
| 1819 | if (PySys_UpdatePath(path, argv0)) { |
| 1820 | /* No way to signal failure, so print exception and exit */ |
| 1821 | PyErr_PrintEx(0); |
| 1822 | exit(1); |
| 1823 | } |