将Python脚本嵌入C中的PAM模块(.so文件)时,ImportError和PyExc_SystemError

Sti*_*y90 3 c numpy pam python-embedding python-2.7

我正在尝试用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 
Run Code Online (Sandbox Code Playgroud)

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 =='\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;
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

每当我使用命令“ sudo”时,“ auth required pam_auth.so”行就会强制系统使用我的模块进行身份验证。(对于exsudo 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)
Run Code Online (Sandbox Code Playgroud)

据我了解,它无法导入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;
}
Run Code Online (Sandbox Code Playgroud)

如果它可以在一般的python嵌入示例下工作,为什么它不能在基于PAM的嵌入示例(使用.so文件的地方)下工作?

PS:我出于特定原因导入numpy。不要问为什么我没有在python脚本中的任何地方使用过,因为这只是我要实现的演示脚本。此外,导入数学不会给出任何导入错误。我也收到SciPY的导入错误。

PPS:Numpy和Scipy软件包可在python脚本中完美运行,并安装在/usr/lib/python2.7/dist-packages/下。我正在使用Ubuntu 14.04。

请帮忙!!!!

Rus*_*art 5

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

假设您说它不会失败,那么必须将其加载。但是,从您得到的错误中我们可以推断出它所包含的符号(例如PyExc_SystemError)对于随后加载的动态库不可见。这是默认的库时使用装载的dlopen() (见RTLD_LOCAL男子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代码中存在这些错误之一,因此避免它听起来是个好主意。

  • 感谢您的详细方法。我添加了标题#include &lt;dlfcn.h&gt;和dlopen(“ libpython2.7.so.1”,RTLD_LAZY | RTLD_GLOBAL); 在Py_Initialize()之前起作用了!对于pam-python,我同意您的看法。但是,我无法通过python编写的PAM脚本中的python文件明确“导入”。例如:在示例pam_permit.py中,我无法显式编写-“导入测试”来导入test.py(路径为/usr/lib/Python2.7),如下所示:import test def pam_sm_authenticate(pamh,flags, argv):/ *根据pam-python示例中的必填行* / return pamh.PAM_SUCCESS有什么建议吗? (2认同)