• 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. (*pamh)->tty = NULL;
    2. (*pamh)->prompt = NULL; /* prompt for pam_get_user() */
    3. (*pamh)->ruser = NULL;
    4. (*pamh)->rhost = NULL;
    5. (*pamh)->authtok = NULL;
    6. (*pamh)->oldauthtok = NULL;
    7. (*pamh)->fail_delay.delay_fn_ptr = NULL;
    8. (*pamh)->former.choice = PAM_NOT_STACKED;
    9. (*pamh)->former.substates = NULL;
    10. #ifdef HAVE_LIBAUDIT
    11. (*pamh)->audit_state = 0;
    12. #endif
    13. (*pamh)->xdisplay = NULL;
    14. (*pamh)->authtok_type = NULL;
    15. (*pamh)->authtok_verified = 0;
    16. memset (&((*pamh)->xauth), 0, sizeof ((*pamh)->xauth));

    这段代码无需做过多解释,就是对pam_handle_t **pamh即struct pam_handle的诸多成员赋值。为了便于理解,再次给出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. };

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

    1. if (((*pamh)->pam_conversation = (struct pam_conv *)
    2. malloc(sizeof(struct pam_conv))) == NULL) {
    3. pam_syslog(*pamh, LOG_CRIT, "pam_start: malloc failed for pam_conv");
    4. _pam_drop((*pamh)->service_name);
    5. _pam_drop((*pamh)->user);
    6. _pam_drop((*pamh)->confdir);
    7. _pam_drop(*pamh);
    8. return (PAM_BUF_ERR);
    9. } else {
    10. memcpy((*pamh)->pam_conversation, pam_conversation,
    11. sizeof(struct pam_conv));
    12. }

    为(*pamh)->pam_conversation分配内存空间。如果失败,释放之前已经分配的service_name、user、confdir等内存空间;如果成功申请到内存,则将由pam_start函数传下来的pam_conversation完全拷贝。

    一般调用pam_start函数的示例如下:

    1. static struct pam_conv conv = {
    2. misc_conv,
    3. NULL
    4. }
    5. /* 初始化,并提供一个回调函数 */
    6. if ((pam_start("login", user_name, &conv, &pamh)) != PAM_SUCCESS)
    7. exit(1);

    接下来来到以下一行代码:

        (*pamh)->data = NULL;

    将(*pamh)->data设置为NULL。

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

  • 相关阅读:
    第10讲:DQL数据查询语句之LIMIT分页查询示例
    在回调之间共享数据
    WebAPI中使用WebService后发布注意要点
    【笔者感悟】笔者的工作感悟【二】
    Android 12(S) 图像显示系统 - 简述Allocator/Mapper HAL服务的获取过程(十五)
    node项目调试
    智能热水器语音控制丨打造智能家居新体验
    任务调度平台在服务器集群上的分布式搭建笔记
    ⑩② 【MySQL索引】详解MySQL`索引`:结构、分类、性能分析、设计及使用规则。
    HTML5期末大作业:游戏网站设计与实现——基于bootstrap响应式游戏资讯网站制作HTML+CSS+JavaScript
  • 原文地址:https://blog.csdn.net/phmatthaus/article/details/134027066