在Python模块中嵌入Python脚本时的ImportError和PyExc_SystemError(.so文件)

我正在尝试用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  #include  #include  #include #include #include #include #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 =='\0') 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的修改。 使用gcc(gcc -shared -o pam_auth.so -fPIC pam_auth.c -I / usr / include / python2.7 -lpython2.7)编译C文件以获取.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”时都使用我的模块进行身份validation。 (适用于鹦鹉螺)

现在的问题是: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  import numpy File "/usr/lib/python2.7/dist-packages/numpy/__init__.py", line 153, in  from . import add_newdocs File "/usr/lib/python2.7/dist-packages/numpy/add_newdocs.py", line 13, in  from numpy.lib import add_newdoc File "/usr/lib/python2.7/dist-packages/numpy/lib/__init__.py", line 8, in  from .type_check import * File "/usr/lib/python2.7/dist-packages/numpy/lib/type_check.py", line 11, in  import numpy.core.numeric as _nx File "/usr/lib/python2.7/dist-packages/numpy/core/__init__.py", line 6, in  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  #include  #include  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。

请帮忙!!!!

我不知道你的问题的答案,但我想知道为什么它没有提前失败。 主机应用程序不知道使用libpython2.7.so.1将需要您的PAM模块,因此必须以某种方式动态加载,否则Py_Initialize()调用将失败并出现相同的错误。

鉴于你说它没有失败,它必须加载。 但是,从您得到的错误中我们可以推断出它包含的符号(例如PyExc_SystemError )对随后加载的动态库是不可见的。 这是使用dlopen()加载库时的默认值(请参阅man 3 dlopen中的 RTLD_LOCAL )。 要覆盖它,您必须将RTLD_GLOBAL传递给dlopen() 。 也许那是你的问题。

关于您的代码的其他评论:

  • 为每个pm_sm _…()调用调用Py_Initialise()将会很昂贵,并且python模块可能会令人惊讶。 这意味着python模块在一次调用中累积的所有数据(比如说语音或用户名)将在下一次调用时被丢弃。 你最好加载libpython2.7.so.1并初始化PAM一次,然后使用pam_set_data()的清理函数在你完成后卸载它。

  • 在一个相关的问题中,你的PAM模块在Python程序中是不可用的,因为你总是调用Py_Initialise() (我假设匹配调用Py_Finalize() )。

  • 如果你的程序没有落到它所做的位置,它就会在printf行上掉线(“结果是%s \ n”,PyString_AsString(pResult)),因为pResult没有初始化。

  • 我想您知道,您在这里使用的所有样板都可以通过pam-python提供Python模块 – 不需要C语言。 既然你显然是用Python编写PAM模块,那么你已经暴露了它所产生的开销,但却忽略了它提供的function,如记录未捕获的Pythonexception。 最重要的是,使用它意味着你可以完全避免使用C. 您的PAM模块将加载到保护机器安全性的程序中 – 程序如login,sudo和xdm / gdm3。 避免C意味着还要避免大量的安全漏洞C程序可能具有Python中不可能的 – 缓冲区溢出,未初始化的指针和访问空闲内存。 由于您在此处发布的C代码中存在其中一个错误,因此避免它听起来是个好主意。