与PAM不同的访问级别

目前我有一个graphics应用程序,有两个访问级别,操作员和pipe理员。 login和身份validation都是自制的,我想切换应用程序使用PAM代替。 我不确定正确的做法是什么。

纠正我,如果我错了,但似乎PAM归结为“是”或“否”检查 – 是的,你可以访问此服务,或者你不能。 没有提供基于哪个用户login的各种访问级别的规定,但是我需要能够告诉谁是操作员和谁是pipe理员,并且我希望能够严格通过PAM进行操作,如果可能的话。

所以我的想法是,我将设置两个不同configuration的服务,运营商为/etc/pam.d/pamdemo-admin ,pipe理员为/etc/pam.d/pamdemo-admin 。 然后,我的应用程序将尝试首先对pamdemo-admin进行身份validation,如果失败,则使用pamdemo 。 如果两者都失败,访问被拒绝。 我在正确的轨道上,还是我完全脱轨?

这里有一些我写的C代码作为概念的certificate。 当我进行login时,我不想提示用户input凭证两次。 我已经知道了,所以它记住了两个pam_start()调用中的用户名,但是我无法从应用程序级别访问pam_get_item(PAM_AUTHTOK)来对密码进行相同的caching。 这是为了让我意识到可能有完全不同的方式来做到这一点。 无论身份validation方法如何 ,无论是用户名/密码还是Kerberos票据或指纹, 我都希望此应用程序能够正常工作

 pam_handle_t *try_login(const char *service, int *retval) { static char * username = NULL; struct pam_conv pam_conversation = { conv, NULL }; pam_handle_t * pamh; *retval = pam_start(service, username, &pam_conversation, &pamh); if (*retval == PAM_SUCCESS) *retval = pam_authenticate(pamh, 0); if (*retval == PAM_SUCCESS) *retval = pam_acct_mgmt (pamh, 0); if (*retval == PAM_SUCCESS) *retval = pam_open_session(pamh, 0); if (username == NULL) { if (pam_get_item(pamh, PAM_USER, (const void **) &username) == PAM_SUCCESS) { username = strdup(username); } } if (*retval != PAM_SUCCESS) { fprintf(stderr, "%s: %s\n", service, pam_strerror(pamh, *retval)); pam_end(pamh, *retval); pamh = NULL; } return pamh; } int main(void) { pam_handle_t *pamh = NULL; int retval; const char *service, *username; if (!pamh) pamh = try_login("pamdemo-admin", &retval); if (!pamh) pamh = try_login("pamdemo", &retval); if (!pamh) { fprintf(stderr, "Access denied.\n"); return 1; } pam_get_item(pamh, PAM_SERVICE, (const void **) &service); pam_get_item(pamh, PAM_USER, (const void **) &username); printf("Logged into %s as %s.\n", service, username); pam_close_session(pamh, 0); pam_end (pamh, retval); return 0; } 

正如所写的这个演示程序重复“密码:”提示。 我不想让它问两次!

我相信一个正确的做法可能是:

  • 设置“pamdemo”服务来完成帐户,认证和会话功能。
  • 设置“pamdemo-admin”服务只做帐户(也可能是会话)功能。 没有认证。
  • 登录时,首先让他们通过“pamdemo”(以确保他们是谁说的) – 如果失败,将他们踢出去。
  • 然后,一旦验证,交给“pamdemo-admin”。 这只是检查,如果他们被允许成为管理员 – 如果他们是,这个检查成功,如果他们不,它不。 由于此检查不会执行身份验证模块,因此不会再次提示您输入密码。

每个咖啡馆的建议,这里是我的解决方案:

 #define PAM_CALL(call) \ do { \ if ((retval = (call)) != PAM_SUCCESS) { \ goto pam_error; \ } \ } while (0) int check_admin_login(const char *user) { pam_handle_t * pamh = NULL; struct pam_conv pam_conversation = { conv, NULL }; int retval; PAM_CALL(pam_start ("pamdemo-admin", user, &pam_conversation, &pamh)); PAM_CALL(pam_acct_mgmt(pamh, 0)); PAM_CALL(pam_end (pamh, retval)); return 1; pam_error: pam_end(pamh, retval); return 0; } int main(void) { pam_handle_t * pamh = NULL; struct pam_conv pam_conversation = { conv, NULL }; int retval; const char * user; int is_admin; PAM_CALL(pam_start ("pamdemo", NULL, &pam_conversation, &pamh)); PAM_CALL(pam_authenticate (pamh, 0)); PAM_CALL(pam_acct_mgmt (pamh, 0)); PAM_CALL(pam_open_session (pamh, 0)); PAM_CALL(pam_get_item (pamh, PAM_USER, (const void **) &user)); is_admin = check_admin_login(user); printf("Logged in as %s (%s).\n", user, is_admin ? "administrator" : "operator"); PAM_CALL(pam_close_session(pamh, 0)); pam_end (pamh, retval); return 0; pam_error: fprintf(stderr, "%s\n", pam_strerror(pamh, retval)); pam_end(pamh, retval); return 1; } 

你可以使用命令“groups”或者“id”来获取用户组,然后grep这些组,如果你先登录管理员,那么它是一个管理员用户,否则它是一个演示用户。

groups / id命令(在Linux上测试)也会得到非本地用户的组(例如PAM / LDAP)

因此,不要检查服务,而要检查用户所属的组。