int duo_check_groups(struct passwd *pw, char **groups, int groups_cnt) { int i; if (groups_cnt > 0) { int matched = 0; if (ga_init(pw->pw_name, pw->pw_gid) < 0) { duo_log(LOG_ERR, "Couldn't get groups", pw->pw_name, NULL, strerror(errno)); return (-1); } for (i = 0; i < groups_cnt; i++) { if (ga_match_pattern_list(groups[i])) { matched = 1; break; } } ga_free(); /* User in configured groups for Duo auth? */ return matched; } else { return 1; } }
static int match_cfg_line_group(const char *grps, int line, const char *user) { int result = 0; struct passwd *pw; if (user == NULL) goto out; if ((pw = getpwnam(user)) == NULL) { debug("Can't match group at line %d because user %.100s does " "not exist", line, user); } else if (ga_init(pw->pw_name, pw->pw_gid) == 0) { debug("Can't Match group because user %.100s not in any group " "at line %d", user, line); } else if (ga_match_pattern_list(grps) != 1) { debug("user %.100s does not match group list %.100s at line %d", user, grps, line); } else { debug("user %.100s matched group list %.100s at line %d", user, grps, line); result = 1; } out: ga_free(); return result; }
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int pam_flags, int argc, const char *argv[]) { struct duo_config cfg; struct passwd *pw; duo_t *duo; duo_code_t code; duopam_const char *config, *cmd, *ip, *p, *service, *user; int i, flags, pam_err; memset(&cfg, 0, sizeof(cfg)); cfg.failmode = DUO_FAIL_SAFE; /* Parse configuration */ config = DUO_CONF; for (i = 0; i < argc; i++) { if (strncmp("conf=", argv[i], 5) == 0) { config = argv[i] + 5; } else if (strcmp("debug", argv[i]) == 0) { options |= PAM_OPT_DEBUG; } else if (strcmp("try_first_pass", argv[i]) == 0) { options |= PAM_OPT_TRY_FIRST_PASS; } else if (strcmp("use_first_pass", argv[i]) == 0) { options |= PAM_OPT_USE_FIRST_PASS|PAM_OPT_TRY_FIRST_PASS; } else if (strcmp("use_uid", argv[i]) == 0) { options |= PAM_OPT_USE_UID; } else if (strcmp("push", argv[i]) == 0) { options |= PAM_OPT_PUSH; } else { _syslog(LOG_ERR, "Invalid pam_duo option: '%s'", argv[i]); return (PAM_SERVICE_ERR); } } i = duo_parse_config(config, __ini_handler, &cfg); if (i == -2) { _syslog(LOG_ERR, "%s must be readable only by user 'root'", config); return (PAM_SERVICE_ERR); } else if (i == -1) { _syslog(LOG_ERR, "Couldn't open %s: %s", config, strerror(errno)); return (PAM_SERVICE_ERR); } else if (i > 0) { _syslog(LOG_ERR, "Parse error in %s, line %d", config, i); return (PAM_SERVICE_ERR); } else if (!cfg.host || !cfg.host[0] || !cfg.skey || !cfg.skey[0] || !cfg.ikey || !cfg.ikey[0]) { _syslog(LOG_ERR, "Missing host, ikey, or skey in %s", config); return (PAM_SERVICE_ERR); } /* Check user */ if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || (pw = getpwnam(user)) == NULL) { return (PAM_USER_UNKNOWN); } /* XXX - Service-specific behavior */ flags = 0; cmd = NULL; if (pam_get_item(pamh, PAM_SERVICE, (duopam_const void **) (duopam_const void *)&service) != PAM_SUCCESS) { return (PAM_SERVICE_ERR); } if (options & PAM_OPT_USE_UID) { /* Check calling user for Duo auth, just like sudo */ if ((pw = getpwuid(getuid())) == NULL) { return (PAM_USER_UNKNOWN); } user = pw->pw_name; } if (strcmp(service, "sshd") == 0) { /* * Disable incremental status reporting for sshd :-( * OpenSSH accumulates PAM_TEXT_INFO from modules to send in * an SSH_MSG_USERAUTH_BANNER post-auth, not real-time! */ flags |= DUO_FLAG_SYNC; } else if (strcmp(service, "sudo") == 0) { cmd = getenv("SUDO_COMMAND"); } /* Check group membership */ if (cfg.groups_cnt > 0) { int matched = 0; if (ga_init(pw->pw_name, pw->pw_gid) < 0) { _log(LOG_ERR, "Couldn't get groups", pw->pw_name, NULL, strerror(errno)); return (PAM_SERVICE_ERR); } for (i = 0; i < cfg.groups_cnt; i++) { if (ga_match_pattern_list(cfg.groups[i])) { matched = 1; break; } } ga_free(); /* User in configured groups for Duo auth? */ if (!matched) return (PAM_SUCCESS); } ip = NULL; pam_get_item(pamh, PAM_RHOST, (duopam_const void **)(duopam_const void *)&ip); /* Honor configured http_proxy */ if (cfg.http_proxy != NULL) { setenv("http_proxy", cfg.http_proxy, 1); } /* Try Duo auth */ if ((duo = duo_open(cfg.host, cfg.ikey, cfg.skey, "pam_duo/" PACKAGE_VERSION, cfg.noverify ? "" : cfg.cafile)) == NULL) { _log(LOG_ERR, "Couldn't open Duo API handle", user, ip, NULL); return (PAM_SERVICE_ERR); } duo_set_conv_funcs(duo, __duo_prompt, __duo_status, pamh); pam_err = PAM_SERVICE_ERR; for (i = 0; i < MAX_RETRIES; i++) { code = duo_login(duo, user, ip, flags, cfg.pushinfo ? cmd : NULL); if (code == DUO_FAIL) { _log(LOG_WARNING, "Failed Duo login", user, ip, duo_geterr(duo)); if ((flags & DUO_FLAG_SYNC) == 0) { pam_info(pamh, "%s", ""); } /* Keep going */ continue; } /* Terminal conditions */ if (code == DUO_OK) { if ((p = duo_geterr(duo)) != NULL) { _log(LOG_WARNING, "Skipped Duo login", user, ip, p); } else { _log(LOG_INFO, "Successful Duo login", user, ip, NULL); } pam_err = PAM_SUCCESS; } else if (code == DUO_ABORT) { _log(LOG_WARNING, "Aborted Duo login", user, ip, duo_geterr(duo)); pam_err = PAM_ABORT; } else if (cfg.failmode == DUO_FAIL_SAFE && (code == DUO_CONN_ERROR || code == DUO_CLIENT_ERROR || code == DUO_SERVER_ERROR)) { _log(LOG_WARNING, "Failsafe Duo login", user, ip, duo_geterr(duo)); pam_err = PAM_SUCCESS; } else { _log(LOG_ERR, "Error in Duo login", user, ip, duo_geterr(duo)); pam_err = PAM_SERVICE_ERR; } break; } if (i == MAX_RETRIES) { pam_err = PAM_MAXTRIES; } duo_close(duo); return (pam_err); }