在 PAM 模块(.so 文件)的 C 中嵌入 Python 脚本时出现 ImportError 和 PyExc_SystemError
ImportError and PyExc_SystemError while embedding Python Script within C for PAM modules (.so files)
我正在尝试用 C 编写一个演示 PAM 模块,它使用 C 概念中的嵌入 Python 到 运行 用 python (2.7) 编写的脚本,在 pam_sm_authenticate()函数,写在C文件中(pam_auth.c).
这是 python 脚本:test.py
import math
import numpy
def test_func():
a = "test"
return a
test.py 的路径是 /usr/lib/Python2.7/ 以便我可以轻松导入它。
这是 C 文件:
#define PAM_SM_AUTH
#define PAM_SM_ACCOUNT
#define PAM_SM_SESSION
#include <security/pam_modules.h>
#include <security/_pam_macros.h>
#include <security/pam_appl.h>
#include<python2.7/Python.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define NOBODY "nobody"
/*PAM Stuffs*/
PAM_EXTERN int pam_sm_authenticate(
pam_handle_t* pamh, int flags, int argc, const char** argv)
{
const char *user;
int retval;
user = NULL;
retval = pam_get_user(pamh, &user, NULL);
if(retval != PAM_SUCCESS)
{
fprintf(stderr, "%s", pam_strerror(pamh, retval));
// return (retval);
}
fprintf(stdout, "retval= %d user=%s\n", retval,user);
if (user == NULL || *user =='[=13=]')
pam_set_item(pamh, PAM_USER, (const char*)NOBODY);
/* Python Wrapper */
// Set PYTHONPATH TO working directory
//int res = setenv("PYTHONPATH",".",1);
//fprintf(stdout, "%d", res);
PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *pResult;
// Initialize the Python Interpreter
Py_Initialize();
// Build the name object
pName = PyString_FromString((char*)"test");
// Load the module object
pModule = PyImport_Import(pName);
// pDict is a borrowed reference
PyErr_Print();
pDict = PyModule_GetDict(pModule);
// pFunc is also a borrowed reference
pFunc = PyDict_GetItemString(pDict, (char*)"test_func");
if (PyCallable_Check(pFunc))
{
pValue=NULL;
PyErr_Print();
pResult=PyObject_CallObject(pFunc,pValue);
PyErr_Print();
}else
{
PyErr_Print();
}
printf("Result is %s\n",PyString_AsString(pResult));
// Clean up
Py_DECREF(pModule);
Py_DECREF(pName);/* */
// Finish the Python Interpreter
Py_Finalize();
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_setcred(
pam_handle_t* pamh, int flags, int argc, const char** argv)
{
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_acct_mgmt(
pam_handle_t* pamh, int flags, int argc, const char** argv)
{
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_open_session(
pam_handle_t* pamh, int flags, int argc, const char** argv)
{
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_close_session(
pam_handle_t* pamh, int flags, int argc, const char** argv)
{
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_chauthtok(
pam_handle_t* pamh, int flags, int argc, const char** argv)
{
return PAM_SUCCESS;
}
C 文件只是 pam_permit.c 的修改版。 C文件使用gcc编译(gcc -shared -o pam_auth.so -fPIC pam_auth.c -I/usr/include/python2.7 -lpython2.7)得到.so文件(pam_auth.so) 并放在文件夹 /lib/security/
中
我在/etc/pam.d中更改了'sudo'文件的PAM配置如下:
#%PAM-1.0
auth required pam_env.so readenv=1 user_readenv=0
auth required pam_env.so readenv=1 envfile=/etc/default/locale user_readenv=0
#@include common-auth #this line is commented to make it use my pam module
auth required pam_auth.so
@include common-account
@include common-session-noninteractive
行"auth required pam_auth.so"强制系统在我每次使用命令"sudo"时使用我的模块进行身份验证。 (对于前 sudo nautilus)
现在的问题是:
C 文件“pModule = PyImport_Import(pName);”中的这一行给出了导入错误,由 PyErr_Print() 打印如下:
stitches@Andromida:~$ sudo nautilus
retval= 0 user=stitches
Traceback (most recent call last):
File "/usr/lib/python2.7/subho_auth.py", line 8, in <module>
import numpy
File "/usr/lib/python2.7/dist-packages/numpy/__init__.py", line 153, in <module>
from . import add_newdocs
File "/usr/lib/python2.7/dist-packages/numpy/add_newdocs.py", line 13, in <module>
from numpy.lib import add_newdoc
File "/usr/lib/python2.7/dist-packages/numpy/lib/__init__.py", line 8, in <module>
from .type_check import *
File "/usr/lib/python2.7/dist-packages/numpy/lib/type_check.py", line 11, in <module>
import numpy.core.numeric as _nx
File "/usr/lib/python2.7/dist-packages/numpy/core/__init__.py", line 6, in <module>
from . import multiarray
ImportError: /usr/lib/python2.7/dist-packages/numpy/core/multiarray.so: undefined symbol: PyExc_SystemError
Segmentation fault (core dumped)
据我所知,它无法导入 test.py 文件中指定的 numpy 库。如何解决ImportError & PyExc_SystemError?
这个问题
如果我 运行 如下所示,python 脚本会发挥作用:
#include <Python.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// Set PYTHONPATH TO working directory
//int res = setenv("PYTHONPATH",".",1);
//fprintf(stdout, "%d", res);
PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *pResult;
// Initialize the Python Interpreter
Py_Initialize();
// Build the name object
pName = PyString_FromString((char*)"test");
// Load the module object
pModule = PyImport_Import(pName);
// pDict is a borrowed reference
PyErr_Print();
pDict = PyModule_GetDict(pModule);
// pFunc is also a borrowed reference
pFunc = PyDict_GetItemString(pDict, (char*)"test_func");
if (PyCallable_Check(pFunc))
{
pValue=NULL;
PyErr_Print();
pResult=PyObject_CallObject(pFunc,pValue);
PyErr_Print();
}else
{
PyErr_Print();
}
printf("Result is %s\n",PyString_AsString(pResult));
// Clean up
Py_DECREF(pModule);
Py_DECREF(pName);/* */
// Finish the Python Interpreter
Py_Finalize();
return 0;
}
如果它在一般 python 嵌入示例下工作,为什么它在基于 PAM 的嵌入示例(使用 .so 文件的地方)下不起作用?
PS:出于特殊原因,我正在导入 numpy。不要问为什么我没有在 python 脚本中的任何地方使用,因为这只是我想要实现的演示脚本。此外,导入数学不会给出任何导入错误。我也收到 SciPY 的导入错误。
PPS:Numpy 和 Scipy 包在 python 脚本中完美运行,安装在 /usr/lib/python2.7/dist-packages/ 下。我正在使用 ubuntu 14.04.
请帮忙!!!!
我不知道你问题的答案,但我想知道为什么它没有早点失败。主机应用程序不知道您的 PAM 模块将需要使用 libpython2.7.so.1,因此必须以某种方式动态加载,否则 Py_Initialize() 调用将失败并显示同样的错误。
鉴于你说它不会在那里失败,它必须被加载。但是,从您收到的错误中,我们可以推断出它包含的符号(例如 PyExc_SystemError)对于随后加载的动态库不可见。这是使用 dlopen() 加载库时的默认设置(参见 RTLD_LOCAL in man 3 dlopen).要覆盖它,您必须将 RTLD_GLOBAL 传递给 dlopen()。也许这是你的问题。
关于您的代码的其他评论:
为每个 pm_sm_...() 调用调用 Py_Initialise() 将是昂贵的并且可能会让 python 模块感到惊讶。这意味着 python 模块在一次调用中积累的所有数据(比如语音或用户名)将在下一次调用时被丢弃。您最好加载 libpython2.7.so.1 并初始化 PAM 一次,然后使用 pam_set_data() 的清理功能在完成后将其卸载。
在相关问题中,您的 PAM 模块无法在 Python 程序中使用,因为您总是调用 Py_Initialise() (我假设匹配调用 Py_Finalize())。
如果你的程序没有掉落在它掉落的地方,它就会掉落在 printf("Result is %s\n",PyString_AsString(pResult )) 因为 pResult 没有初始化。
我想你知道,这里所有的样板都可以让你在 Python 中编写 PAM 模块,由 pam-python 提供——不需要 C。由于您显然是在 Python 中编写 PAM 模块,因此您已经暴露了它所产生的开销,但错过了它提供的功能,例如记录未捕获的 Python 异常。最重要的是,使用它意味着您可以完全避免使用 C。您的 PAM 模块将被加载到保护机器安全的程序中 - 像 login、sudo 和 xdm/gdm3 这样的程序。避免 C 也意味着避免 C 程序可能存在的大量安全漏洞,这些漏洞在 Python 中是不可能的——缓冲区溢出、未初始化的指针和访问释放的内存。由于您在此处发布的 C 代码中存在其中一个错误,因此避免它听起来是个好主意。
我正在尝试用 C 编写一个演示 PAM 模块,它使用 C 概念中的嵌入 Python 到 运行 用 python (2.7) 编写的脚本,在 pam_sm_authenticate()函数,写在C文件中(pam_auth.c).
这是 python 脚本:test.py
import math
import numpy
def test_func():
a = "test"
return a
test.py 的路径是 /usr/lib/Python2.7/ 以便我可以轻松导入它。
这是 C 文件:
#define PAM_SM_AUTH
#define PAM_SM_ACCOUNT
#define PAM_SM_SESSION
#include <security/pam_modules.h>
#include <security/_pam_macros.h>
#include <security/pam_appl.h>
#include<python2.7/Python.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define NOBODY "nobody"
/*PAM Stuffs*/
PAM_EXTERN int pam_sm_authenticate(
pam_handle_t* pamh, int flags, int argc, const char** argv)
{
const char *user;
int retval;
user = NULL;
retval = pam_get_user(pamh, &user, NULL);
if(retval != PAM_SUCCESS)
{
fprintf(stderr, "%s", pam_strerror(pamh, retval));
// return (retval);
}
fprintf(stdout, "retval= %d user=%s\n", retval,user);
if (user == NULL || *user =='[=13=]')
pam_set_item(pamh, PAM_USER, (const char*)NOBODY);
/* Python Wrapper */
// Set PYTHONPATH TO working directory
//int res = setenv("PYTHONPATH",".",1);
//fprintf(stdout, "%d", res);
PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *pResult;
// Initialize the Python Interpreter
Py_Initialize();
// Build the name object
pName = PyString_FromString((char*)"test");
// Load the module object
pModule = PyImport_Import(pName);
// pDict is a borrowed reference
PyErr_Print();
pDict = PyModule_GetDict(pModule);
// pFunc is also a borrowed reference
pFunc = PyDict_GetItemString(pDict, (char*)"test_func");
if (PyCallable_Check(pFunc))
{
pValue=NULL;
PyErr_Print();
pResult=PyObject_CallObject(pFunc,pValue);
PyErr_Print();
}else
{
PyErr_Print();
}
printf("Result is %s\n",PyString_AsString(pResult));
// Clean up
Py_DECREF(pModule);
Py_DECREF(pName);/* */
// Finish the Python Interpreter
Py_Finalize();
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_setcred(
pam_handle_t* pamh, int flags, int argc, const char** argv)
{
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_acct_mgmt(
pam_handle_t* pamh, int flags, int argc, const char** argv)
{
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_open_session(
pam_handle_t* pamh, int flags, int argc, const char** argv)
{
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_close_session(
pam_handle_t* pamh, int flags, int argc, const char** argv)
{
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_chauthtok(
pam_handle_t* pamh, int flags, int argc, const char** argv)
{
return PAM_SUCCESS;
}
C 文件只是 pam_permit.c 的修改版。 C文件使用gcc编译(gcc -shared -o pam_auth.so -fPIC pam_auth.c -I/usr/include/python2.7 -lpython2.7)得到.so文件(pam_auth.so) 并放在文件夹 /lib/security/
中我在/etc/pam.d中更改了'sudo'文件的PAM配置如下:
#%PAM-1.0
auth required pam_env.so readenv=1 user_readenv=0
auth required pam_env.so readenv=1 envfile=/etc/default/locale user_readenv=0
#@include common-auth #this line is commented to make it use my pam module
auth required pam_auth.so
@include common-account
@include common-session-noninteractive
行"auth required pam_auth.so"强制系统在我每次使用命令"sudo"时使用我的模块进行身份验证。 (对于前 sudo nautilus)
现在的问题是: C 文件“pModule = PyImport_Import(pName);”中的这一行给出了导入错误,由 PyErr_Print() 打印如下:
stitches@Andromida:~$ sudo nautilus
retval= 0 user=stitches
Traceback (most recent call last):
File "/usr/lib/python2.7/subho_auth.py", line 8, in <module>
import numpy
File "/usr/lib/python2.7/dist-packages/numpy/__init__.py", line 153, in <module>
from . import add_newdocs
File "/usr/lib/python2.7/dist-packages/numpy/add_newdocs.py", line 13, in <module>
from numpy.lib import add_newdoc
File "/usr/lib/python2.7/dist-packages/numpy/lib/__init__.py", line 8, in <module>
from .type_check import *
File "/usr/lib/python2.7/dist-packages/numpy/lib/type_check.py", line 11, in <module>
import numpy.core.numeric as _nx
File "/usr/lib/python2.7/dist-packages/numpy/core/__init__.py", line 6, in <module>
from . import multiarray
ImportError: /usr/lib/python2.7/dist-packages/numpy/core/multiarray.so: undefined symbol: PyExc_SystemError
Segmentation fault (core dumped)
据我所知,它无法导入 test.py 文件中指定的 numpy 库。如何解决ImportError & PyExc_SystemError?
这个问题如果我 运行 如下所示,python 脚本会发挥作用:
#include <Python.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// Set PYTHONPATH TO working directory
//int res = setenv("PYTHONPATH",".",1);
//fprintf(stdout, "%d", res);
PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *pResult;
// Initialize the Python Interpreter
Py_Initialize();
// Build the name object
pName = PyString_FromString((char*)"test");
// Load the module object
pModule = PyImport_Import(pName);
// pDict is a borrowed reference
PyErr_Print();
pDict = PyModule_GetDict(pModule);
// pFunc is also a borrowed reference
pFunc = PyDict_GetItemString(pDict, (char*)"test_func");
if (PyCallable_Check(pFunc))
{
pValue=NULL;
PyErr_Print();
pResult=PyObject_CallObject(pFunc,pValue);
PyErr_Print();
}else
{
PyErr_Print();
}
printf("Result is %s\n",PyString_AsString(pResult));
// Clean up
Py_DECREF(pModule);
Py_DECREF(pName);/* */
// Finish the Python Interpreter
Py_Finalize();
return 0;
}
如果它在一般 python 嵌入示例下工作,为什么它在基于 PAM 的嵌入示例(使用 .so 文件的地方)下不起作用?
PS:出于特殊原因,我正在导入 numpy。不要问为什么我没有在 python 脚本中的任何地方使用,因为这只是我想要实现的演示脚本。此外,导入数学不会给出任何导入错误。我也收到 SciPY 的导入错误。
PPS:Numpy 和 Scipy 包在 python 脚本中完美运行,安装在 /usr/lib/python2.7/dist-packages/ 下。我正在使用 ubuntu 14.04.
请帮忙!!!!
我不知道你问题的答案,但我想知道为什么它没有早点失败。主机应用程序不知道您的 PAM 模块将需要使用 libpython2.7.so.1,因此必须以某种方式动态加载,否则 Py_Initialize() 调用将失败并显示同样的错误。
鉴于你说它不会在那里失败,它必须被加载。但是,从您收到的错误中,我们可以推断出它包含的符号(例如 PyExc_SystemError)对于随后加载的动态库不可见。这是使用 dlopen() 加载库时的默认设置(参见 RTLD_LOCAL in man 3 dlopen).要覆盖它,您必须将 RTLD_GLOBAL 传递给 dlopen()。也许这是你的问题。
关于您的代码的其他评论:
为每个 pm_sm_...() 调用调用 Py_Initialise() 将是昂贵的并且可能会让 python 模块感到惊讶。这意味着 python 模块在一次调用中积累的所有数据(比如语音或用户名)将在下一次调用时被丢弃。您最好加载 libpython2.7.so.1 并初始化 PAM 一次,然后使用 pam_set_data() 的清理功能在完成后将其卸载。
在相关问题中,您的 PAM 模块无法在 Python 程序中使用,因为您总是调用 Py_Initialise() (我假设匹配调用 Py_Finalize())。
如果你的程序没有掉落在它掉落的地方,它就会掉落在 printf("Result is %s\n",PyString_AsString(pResult )) 因为 pResult 没有初始化。
我想你知道,这里所有的样板都可以让你在 Python 中编写 PAM 模块,由 pam-python 提供——不需要 C。由于您显然是在 Python 中编写 PAM 模块,因此您已经暴露了它所产生的开销,但错过了它提供的功能,例如记录未捕获的 Python 异常。最重要的是,使用它意味着您可以完全避免使用 C。您的 PAM 模块将被加载到保护机器安全的程序中 - 像 login、sudo 和 xdm/gdm3 这样的程序。避免 C 也意味着避免 C 程序可能存在的大量安全漏洞,这些漏洞在 Python 中是不可能的——缓冲区溢出、未初始化的指针和访问释放的内存。由于您在此处发布的 C 代码中存在其中一个错误,因此避免它听起来是个好主意。