• PAM从入门到精通(二十一)


    接前一篇文章:PAM从入门到精通(二十)

    本文参考:

    《The Linux-PAM Application Developers' Guide》

    先再来重温一下PAM系统架构:

    更加形象的形式:

    七、PAM-API各函数源码详解

    前边的文章讲解了各PAM-API函数以及总体流程,但是也只是从接口层面介绍的,并没有深入到代码层面。从本篇文章开始,将对于各个接口函数从源码级进行讲解,以使大家不但知其然,还要知其所以然。

    1. pam_start函数

    上回讲到_pam_start_internal函数的第一部分,本文继续往下进行讲解。为了便于理解,再次贴出_pam_start_internal函数源码。在libpam/pam_start.c中,如下所示:

    1. static int _pam_start_internal (
    2. const char *service_name,
    3. const char *user,
    4. const struct pam_conv *pam_conversation,
    5. const char *confdir,
    6. pam_handle_t **pamh)
    7. {
    8. D(("called pam_start: [%s] [%s] [%p] [%p]"
    9. ,service_name, user, pam_conversation, pamh));
    10. if (pamh == NULL) {
    11. pam_syslog(NULL, LOG_CRIT,
    12. "pam_start: invalid argument: pamh == NULL");
    13. return (PAM_SYSTEM_ERR);
    14. }
    15. if (service_name == NULL) {
    16. pam_syslog(NULL, LOG_CRIT,
    17. "pam_start: invalid argument: service == NULL");
    18. return (PAM_SYSTEM_ERR);
    19. }
    20. if (pam_conversation == NULL) {
    21. pam_syslog(NULL, LOG_CRIT,
    22. "pam_start: invalid argument: conv == NULL");
    23. return (PAM_SYSTEM_ERR);
    24. }
    25. if ((*pamh = calloc(1, sizeof(**pamh))) == NULL) {
    26. pam_syslog(NULL, LOG_CRIT, "pam_start: calloc failed for *pamh");
    27. return (PAM_BUF_ERR);
    28. }
    29. /* All service names should be files below /etc/pam.d and nothing
    30. else. Forbid paths. */
    31. if (strrchr(service_name, '/') != NULL)
    32. service_name = strrchr(service_name, '/') + 1;
    33. /* Mark the caller as the application - permission to do certain
    34. things is limited to a module or an application */
    35. __PAM_TO_APP(*pamh);
    36. if (((*pamh)->service_name = _pam_strdup(service_name)) == NULL) {
    37. pam_syslog(*pamh, LOG_CRIT,
    38. "pam_start: _pam_strdup failed for service name");
    39. _pam_drop(*pamh);
    40. return (PAM_BUF_ERR);
    41. } else {
    42. char *tmp;
    43. for (tmp=(*pamh)->service_name; *tmp; ++tmp)
    44. *tmp = tolower(*tmp); /* require lower case */
    45. }
    46. if (user) {
    47. if (((*pamh)->user = _pam_strdup(user)) == NULL) {
    48. pam_syslog(*pamh, LOG_CRIT,
    49. "pam_start: _pam_strdup failed for user");
    50. _pam_drop((*pamh)->service_name);
    51. _pam_drop(*pamh);
    52. return (PAM_BUF_ERR);
    53. }
    54. } else
    55. (*pamh)->user = NULL;
    56. if (confdir) {
    57. if (((*pamh)->confdir = _pam_strdup(confdir)) == NULL) {
    58. pam_syslog(*pamh, LOG_CRIT,
    59. "pam_start: _pam_strdup failed for confdir");
    60. _pam_drop((*pamh)->service_name);
    61. _pam_drop((*pamh)->user);
    62. _pam_drop(*pamh);
    63. return (PAM_BUF_ERR);
    64. }
    65. } else
    66. (*pamh)->confdir = NULL;
    67. (*pamh)->tty = NULL;
    68. (*pamh)->prompt = NULL; /* prompt for pam_get_user() */
    69. (*pamh)->ruser = NULL;
    70. (*pamh)->rhost = NULL;
    71. (*pamh)->authtok = NULL;
    72. (*pamh)->oldauthtok = NULL;
    73. (*pamh)->fail_delay.delay_fn_ptr = NULL;
    74. (*pamh)->former.choice = PAM_NOT_STACKED;
    75. (*pamh)->former.substates = NULL;
    76. #ifdef HAVE_LIBAUDIT
    77. (*pamh)->audit_state = 0;
    78. #endif
    79. (*pamh)->xdisplay = NULL;
    80. (*pamh)->authtok_type = NULL;
    81. (*pamh)->authtok_verified = 0;
    82. memset (&((*pamh)->xauth), 0, sizeof ((*pamh)->xauth));
    83. if (((*pamh)->pam_conversation = (struct pam_conv *)
    84. malloc(sizeof(struct pam_conv))) == NULL) {
    85. pam_syslog(*pamh, LOG_CRIT, "pam_start: malloc failed for pam_conv");
    86. _pam_drop((*pamh)->service_name);
    87. _pam_drop((*pamh)->user);
    88. _pam_drop((*pamh)->confdir);
    89. _pam_drop(*pamh);
    90. return (PAM_BUF_ERR);
    91. } else {
    92. memcpy((*pamh)->pam_conversation, pam_conversation,
    93. sizeof(struct pam_conv));
    94. }
    95. (*pamh)->data = NULL;
    96. if ( _pam_make_env(*pamh) != PAM_SUCCESS ) {
    97. pam_syslog(*pamh,LOG_ERR,"pam_start: failed to initialize environment");
    98. _pam_drop((*pamh)->pam_conversation);
    99. _pam_drop((*pamh)->service_name);
    100. _pam_drop((*pamh)->user);
    101. _pam_drop((*pamh)->confdir);
    102. _pam_drop(*pamh);
    103. return PAM_ABORT;
    104. }
    105. _pam_reset_timer(*pamh); /* initialize timer support */
    106. _pam_start_handlers(*pamh); /* cannot fail */
    107. /* According to the SunOS man pages, loading modules and resolving
    108. * symbols happens on the first call from the application. */
    109. if ( _pam_init_handlers(*pamh) != PAM_SUCCESS ) {
    110. pam_syslog(*pamh, LOG_ERR, "pam_start: failed to initialize handlers");
    111. _pam_drop_env(*pamh); /* purge the environment */
    112. _pam_drop((*pamh)->pam_conversation);
    113. _pam_drop((*pamh)->service_name);
    114. _pam_drop((*pamh)->user);
    115. _pam_drop((*pamh)->confdir);
    116. _pam_drop(*pamh);
    117. return PAM_ABORT;
    118. }
    119. D(("exiting pam_start successfully"));
    120. return PAM_SUCCESS;
    121. }

    做完第一步参数检查的工作后,接下来正式开始进行实际功能实现。

    先是进行核心结构体的实例分配,代码片段如下:

    1. if ((*pamh = calloc(1, sizeof(**pamh))) == NULL) {
    2. pam_syslog(NULL, LOG_CRIT, "pam_start: calloc failed for *pamh");
    3. return (PAM_BUF_ERR);
    4. }

    一般调用pam_start函数的时候,传入的参数都是这样:

    1. pam_handle_t *pamh = NULL;
    2. /* 初始化,并提供一个回调函数 */
    3. if ((pam_start("login", user_name, &conv, &pamh)) != PAM_SUCCESS)
    4. exit(1);

    传入的是pam_handle_t即struct pam_handle的指针的地址,也就是指针的指针。之所以传入指针的地址,就是为了在这里使该指针指向calloc动态分配的空间。再来重温一下struct pam_handle的定义,在libpam/pam_private.h中,如下:

    1. struct pam_handle {
    2. char *authtok;
    3. unsigned caller_is;
    4. struct pam_conv *pam_conversation;
    5. char *oldauthtok;
    6. char *prompt; /* for use by pam_get_user() */
    7. char *service_name;
    8. char *user;
    9. char *rhost;
    10. char *ruser;
    11. char *tty;
    12. char *xdisplay;
    13. char *authtok_type; /* PAM_AUTHTOK_TYPE */
    14. struct pam_data *data;
    15. struct pam_environ *env; /* structure to maintain environment list */
    16. struct _pam_fail_delay fail_delay; /* helper function for easy delays */
    17. struct pam_xauth_data xauth; /* auth info for X display */
    18. struct service handlers;
    19. struct _pam_former_state former; /* library state - support for
    20. event driven applications */
    21. const char *mod_name; /* Name of the module currently executed */
    22. int mod_argc; /* Number of module arguments */
    23. char **mod_argv; /* module arguments */
    24. int choice; /* Which function we call from the module */
    25. #ifdef HAVE_LIBAUDIT
    26. int audit_state; /* keep track of reported audit messages */
    27. #endif
    28. int authtok_verified;
    29. char *confdir;
    30. };

    分配了pam_handle_t类型的内存后,_pam_start_internal函数接下来对参数service_name作进一步处理,代码片段如下:

    1. /* All service names should be files below /etc/pam.d and nothing
    2. else. Forbid paths. */
    3. if (strrchr(service_name, '/') != NULL)
    4. service_name = strrchr(service_name, '/') + 1;

    如果传入的service_name字符串中包含'/',则只取'/'之后的名称。

    接下来来到以下代码片段:

    1. /* Mark the caller as the application - permission to do certain
    2. things is limited to a module or an application */
    3. __PAM_TO_APP(*pamh);

    __PAM_TO_APP是一个宏,在libpam/pam_private.h中定义,代码如下:

    1. #define __PAM_TO_APP(pamh) \
    2. do { (pamh)->caller_is = _PAM_CALLED_FROM_APP; } while (0)

    这句代码很好理解,就是将上边分配的pamh对象的caller_is(unsigned caller_is)成员赋值为_PAM_CALLED_FROM_APP。_PAM_CALLED_FROM_APP在同文件中定义,代码如下:

    1. /* used to work out where control currently resides (in an application
    2. or in a module) */
    3. #define _PAM_CALLED_FROM_MODULE 1
    4. #define _PAM_CALLED_FROM_APP 2

    综上,这段代码的意思就是将调用者(控制权)标记为应用(Application)侧。

    _pam_start_internal函数的其余部分将在后续文章中继续讲解。

  • 相关阅读:
    受电诱骗快充取电芯片XSP08:PD+QC+华为+三星多种协议9V12V15V20V
    API接口接入1688电商数据平台获取商品详情数据示例
    uni-app:实现密码框内容展示与隐藏
    20个短视频素材网站
    Linux 内存性能指标
    MFC自定义消息的实现方法----(线程向主对话框发送消息)、MFC不能用UpdateData的解决方法
    python安装selselenium,chromedriver,秒杀脚本教程
    Leetcode.19 删除链表的倒数第 N 个结点
    plink如何更新表型数据
    MQTT第一话 -- Docker安装emqx以及Springboot集成emqx
  • 原文地址:https://blog.csdn.net/phmatthaus/article/details/133988653