PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { const char *username; const char *password; int retval = PAM_AUTH_ERR; /* parse arguments */ ctrl = _pam_parse(argc, argv); /* Get the username */ retval = pam_get_user(pamh, &username, NULL); if ((retval != PAM_SUCCESS) || (!username)) { if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_DEBUG,"can not get the username"); return PAM_SERVICE_ERR; } /* Converse just to be sure we have the password */ retval = conversation(pamh); if (retval != PAM_SUCCESS) { _pam_log(LOG_ERR, "could not obtain password for `%s'", username); return -2; } /* Get the password */ retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&password); if (retval != PAM_SUCCESS) { _pam_log(LOG_ERR, "Could not retrive user's password"); return -2; } if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_INFO, "Verify user `%s' with password `%s'", username, password); /* Now use the username to look up password in the database file */ retval = user_lookup(username, password); switch (retval) { case -2: /* some sort of system error. The log was already printed */ return PAM_SERVICE_ERR; case -1: /* incorrect password */ _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password)", username); return PAM_AUTH_ERR; case 1: /* the user does not exist in the database */ if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_NOTICE, "user `%s' not found in the database", username); return PAM_USER_UNKNOWN; case 0: /* Otherwise, the authentication looked good */ _pam_log(LOG_NOTICE, "user '%s' granted acces", username); return PAM_SUCCESS; default: /* we don't know anything about this return value */ _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'", retval, username); return PAM_SERVICE_ERR; } /* should not be reached */ return PAM_IGNORE; }
/* * Send an accounting record to the TACACS+ server. * We send the start/stop accounting records even if the user is not known * to the TACACS+ server. This seems non-intuitive, but it's the way * this code is written to work. */ int _pam_account(pam_handle_t *pamh, int argc, const char **argv, int type, char *cmd) { int retval; int ctrl; char *user = NULL; char *tty = NULL; char *r_addr = NULL; char *typemsg; int status = PAM_SESSION_ERR; int srv_i, tac_fd; typemsg = tac_acct_flag2str(type); ctrl = _pam_parse (argc, argv); if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: [%s] called (pam_tacplus v%u.%u.%u)", __func__, typemsg, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); _pam_get_user(pamh, &user); if (user == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: username [%s] obtained", __func__, user); if (!task_id) #if defined(HAVE_OPENSSL_RAND_H) && defined(HAVE_LIBCRYPTO) RAND_pseudo_bytes((unsigned char *) &task_id, sizeof(task_id)); #else task_id = (short unsigned int) tac_magic(); #endif _pam_get_terminal(pamh, &tty); if(!strncmp(tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: tty [%s] obtained", __func__, tty); _pam_get_rhost(pamh, &r_addr); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: rhost [%s] obtained", __func__, r_addr); /* checks for specific data required by TACACS+, which should be supplied in command line */ if(tac_protocol[0] == '\0') { _pam_log (LOG_ERR, "ACC: TACACS+ protocol type not configured"); return PAM_AUTH_ERR; } /* when this module is called from within pppd or other application dealing with serial lines, it is likely that we will get hit with signal caused by modem hangup; this is important only for STOP packets, it's relatively rare that modem hangs up on accounting start */ if(type == TAC_PLUS_ACCT_FLAG_STOP) { signal(SIGALRM, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); } /* * If PAM_SESSION_ERR is used, then the pam config can't * ignore server failures, so use PAM_AUTHINFO_UNAVAIL. * * We have to make a new connection each time, because libtac is single * threaded (doesn't support multiple connects at the same time due to * use of globals)), and doesn't have support for persistent connections. * That's fixable, but not worth the effort at this point. * * TODO: this should be converted to use do_tac_connect eventually. */ status = PAM_AUTHINFO_UNAVAIL; for(srv_i = 0; srv_i < tac_srv_no; srv_i++) { tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, NULL, __vrfname); if (tac_fd < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (fd)", __func__, typemsg); continue; } if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: connected with fd=%d to srv[%d] %s", __func__, tac_fd, srv_i, tac_srv[srv_i].addr ? tac_ntop(tac_srv[srv_i].addr->ai_addr) : "not set"); retval = _pam_send_account(tac_fd, type, user, tty, r_addr, cmd); if (retval < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (acct)", __func__, typemsg); } else { status = PAM_SUCCESS; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: [%s] for [%s] sent", __func__, typemsg, user); } if ((status == PAM_SUCCESS) && !(ctrl & PAM_TAC_ACCT)) { /* do not send acct start/stop packets to _all_ servers */ break; } } if (type == TAC_PLUS_ACCT_FLAG_STOP) { signal(SIGALRM, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); } return status; }
/* no-op function for future use */ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, const char **argv) { int ctrl, retval; char *user; char *pass; char *tty; char *r_addr; const void *pam_pass = NULL; int srv_i; int tac_fd, status, msg, communicating; user = pass = tty = r_addr = NULL; ctrl = _pam_parse(argc, argv); if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)" , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); syslog(LOG_DEBUG, "%s(flags=%d, argc=%d)", __func__, flags, argc); if ( (pam_get_item(pamh, PAM_OLDAUTHTOK, &pam_pass) == PAM_SUCCESS) && (pam_pass != NULL) ) { if ((pass = strdup(pam_pass)) == NULL) return PAM_BUF_ERR; } else { pass = strdup(""); } if ((user = _pam_get_user(pamh)) == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: user [%s] obtained", __FUNCTION__, user); tty = _pam_get_terminal(pamh); if (!strncmp(tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: tty [%s] obtained", __FUNCTION__, tty); r_addr = _pam_get_rhost(pamh); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: rhost [%s] obtained", __FUNCTION__, r_addr); if (PAM_SILENT == (flags & PAM_SILENT)) { status = PAM_AUTHTOK_ERR; goto finish; } status = PAM_TRY_AGAIN; for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i ); tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, NULL, tac_timeout); if (tac_fd < 0) { _pam_log(LOG_ERR, "connection failed srv %d: %m", srv_i); continue; } if (PAM_PRELIM_CHECK == (flags & PAM_PRELIM_CHECK)) { if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG)) syslog(LOG_DEBUG, "%s: finishing PAM_PRELIM_CHECK with srv %d", __FUNCTION__, srv_i); close(tac_fd); status = PAM_SUCCESS; goto finish; } if (tac_authen_send(tac_fd, user, "", tty, r_addr, TAC_PLUS_AUTHEN_CHPASS) < 0) { close(tac_fd); _pam_log(LOG_ERR, "error sending auth req to TACACS+ server"); continue; } communicating = 1; while (communicating) { struct areply re = { .attr = NULL, .msg = NULL, status = 0, flags = 0 }; struct pam_message conv_msg = { .msg_style = 0, .msg = NULL }; struct pam_response *resp = NULL; msg = tac_authen_read(tac_fd, &re); if (NULL != re.msg) { conv_msg.msg = re.msg; } /* talk the protocol */ switch (msg) { case TAC_PLUS_AUTHEN_STATUS_PASS: /* success */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_PASS"); if (NULL != conv_msg.msg) { int retval = -1; conv_msg.msg_style = PAM_TEXT_INFO; retval = converse(pamh, 1, &conv_msg, &resp); if (PAM_SUCCESS == retval) { if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG)) syslog(LOG_DEBUG, "send msg=\"%s\"", conv_msg.msg); } else { _pam_log(LOG_WARNING, "%s: error sending msg=\"%s\", retval=%d", __FUNCTION__, conv_msg.msg, retval); } } status = PAM_SUCCESS; communicating = 0; active_server.addr = tac_srv[srv_i].addr; active_server.key = tac_srv[srv_i].key; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: active srv %d", __FUNCTION__, srv_i); break; case TAC_PLUS_AUTHEN_STATUS_FAIL: if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_FAIL"); if (NULL != conv_msg.msg) { int retval = -1; conv_msg.msg_style = PAM_ERROR_MSG; retval = converse(pamh, 1, &conv_msg, &resp); if (PAM_SUCCESS == retval) { if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG)) syslog(LOG_DEBUG, "send msg=\"%s\"", conv_msg.msg); } else { _pam_log(LOG_WARNING, "%s: error sending msg=\"%s\", retval=%d", __FUNCTION__, conv_msg.msg, retval); } } status = PAM_AUTHTOK_ERR; communicating = 0; _pam_log(LOG_ERR, "chauthtok failed: %d", msg); break; case TAC_PLUS_AUTHEN_STATUS_GETDATA: if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG)) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETDATA"); if (NULL != conv_msg.msg) { int retval = -1; int echo_off = (0x1 == (re.flags & 0x1)); conv_msg.msg_style = echo_off ? PAM_PROMPT_ECHO_OFF : PAM_PROMPT_ECHO_ON; retval = converse(pamh, 1, &conv_msg, &resp); if (PAM_SUCCESS == retval) { if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG)) syslog(LOG_DEBUG, "sent msg=\"%s\", resp=\"%s\"", conv_msg.msg, resp->resp); if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG)) syslog(LOG_DEBUG, "%s: calling tac_cont_send", __FUNCTION__); if (0 > tac_cont_send_seq(tac_fd, resp->resp, re.seq_no + 1)) { _pam_log(LOG_ERR, "error sending continue req to TACACS+ server"); communicating = 0; } } else { _pam_log(LOG_WARNING, "%s: error sending msg=\"%s\", retval=%d", __FUNCTION__, conv_msg.msg, retval); communicating = 0; } } else { syslog(LOG_ERR, "GETDATA response with no message, returning PAM_TRY_AGAIN"); communicating = 0; } break; case TAC_PLUS_AUTHEN_STATUS_GETUSER: /* not implemented */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETUSER"); communicating = 0; break; case TAC_PLUS_AUTHEN_STATUS_GETPASS: if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETPASS"); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: calling tac_cont_send", __FUNCTION__); if (tac_cont_send(tac_fd, pass) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); communicating = 0; break; } /* continue the while loop; go read tac response */ break; case TAC_PLUS_AUTHEN_STATUS_RESTART: /* try it again */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_RESTART"); /* * not implemented * WdJ: I *think* you can just do tac_authen_send(user, pass) again * but I'm not sure */ communicating = 0; break; case TAC_PLUS_AUTHEN_STATUS_ERROR: /* server has problems */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_ERROR"); communicating = 0; break; case TAC_PLUS_AUTHEN_STATUS_FOLLOW: /* server tells to try a different server address */ /* not implemented */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_FOLLOW"); communicating = 0; break; default: if (msg < 0) { /* connection error */ communicating = 0; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "error communicating with tacacs server"); break; } /* unknown response code */ communicating = 0; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: unknown response 0x%02x", msg); } if (NULL != resp) { free(resp->resp); free(resp); } free(re.msg); } /* end while(communicating) */ close(tac_fd); if (status == PAM_SUCCESS || status == PAM_AUTHTOK_ERR) break; } finish: if (status != PAM_SUCCESS && status != PAM_AUTHTOK_ERR) _pam_log(LOG_ERR, "no more servers to connect"); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: exit with pam status: %d", __FUNCTION__, status); if (NULL != pass) { bzero(pass, strlen(pass)); free(pass); pass = NULL; } return status; } /* pam_sm_chauthtok */ #endif #ifdef PAM_STATIC struct pam_module _pam_tacplus_modstruct { "pam_tacplus", pam_sm_authenticate, pam_sm_setcred, pam_sm_acct_mgmt, pam_sm_open_session, pam_sm_close_session, #ifdef PAM_SM_PASSWORD pam_sm_chauthtok #else NULL #endif };
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc ,const char **argv) { int ctrl; const char *username; char *fromsu; struct passwd *pwd, *tpwd; struct group *grp; int retval = PAM_AUTH_ERR; char use_group[BUFSIZ]; /* Init the optional group */ bzero(use_group,BUFSIZ); ctrl = _pam_parse(argc, argv, use_group); retval = pam_get_user(pamh,&username,NULL); if ((retval != PAM_SUCCESS) || (!username)) { if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_DEBUG,"can not get the username"); return PAM_SERVICE_ERR; } /* su to a uid 0 account ? */ pwd = getpwnam(username); if (!pwd) { if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_NOTICE,"unknown user %s",username); return PAM_USER_UNKNOWN; } /* Now we know that the username exists, pass on to other modules... * the call to pam_get_user made this obsolete, so is commented out * * pam_set_item(pamh,PAM_USER,(const void *)username); */ /* is this user an UID 0 account ? */ if(pwd->pw_uid) { /* no need to check for wheel */ return PAM_IGNORE; } if (ctrl & PAM_USE_UID_ARG) { tpwd = getpwuid(getuid()); if (!tpwd) { if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_NOTICE,"who is running me ?!"); return PAM_SERVICE_ERR; } fromsu = tpwd->pw_name; } else { fromsu = getlogin(); if (!fromsu) { if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_NOTICE,"who is running me ?!"); return PAM_SERVICE_ERR; } } if (!use_group[0]) { if ((grp = getgrnam("wheel")) == NULL) { grp = getgrgid(0); } } else grp = getgrnam(use_group); if (!grp || !grp->gr_mem) { if (ctrl & PAM_DEBUG_ARG) { if (!use_group[0]) _pam_log(LOG_NOTICE,"no members in a GID 0 group"); else _pam_log(LOG_NOTICE,"no members in '%s' group",use_group); } if (ctrl & PAM_DENY_ARG) /* if this was meant to deny access to the members * of this group and the group does not exist, allow * access */ return PAM_IGNORE; else return PAM_AUTH_ERR; } if (is_on_list(grp->gr_mem, fromsu)) { if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_NOTICE,"Access %s to '%s' for '%s'", (ctrl & PAM_DENY_ARG)?"denied":"granted", fromsu,username); if (ctrl & PAM_DENY_ARG) return PAM_PERM_DENIED; else if (ctrl & PAM_TRUST_ARG) return PAM_SUCCESS; else return PAM_IGNORE; } if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_NOTICE,"Access %s for '%s' to '%s'", (ctrl & PAM_DENY_ARG)?"granted":"denied",fromsu,username); if (ctrl & PAM_DENY_ARG) return PAM_SUCCESS; else return PAM_PERM_DENIED; }
/* now the session stuff */ PAM_EXTERN int pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv) { int retval; int i; int glob_rc; char *user_name; struct passwd *pwd; int ctrl; struct pam_limit_s plstruct; struct pam_limit_s *pl = &plstruct; glob_t globbuf; const char *oldlocale; D(("called.")); memset(pl, 0, sizeof(*pl)); memset(&globbuf, 0, sizeof(globbuf)); ctrl = _pam_parse(pamh, argc, argv, pl); retval = pam_get_item( pamh, PAM_USER, (void*) &user_name ); if ( user_name == NULL || retval != PAM_SUCCESS ) { pam_syslog(pamh, LOG_CRIT, "open_session - error recovering username"); return PAM_SESSION_ERR; } pwd = pam_modutil_getpwnam(pamh, user_name); if (!pwd) { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_WARNING, "open_session username '%s' does not exist", user_name); return PAM_USER_UNKNOWN; } retval = init_limits(pamh, pl, ctrl); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_WARNING, "cannot initialize"); return PAM_ABORT; } retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl); if (retval == PAM_IGNORE) { D(("the configuration file ('%s') has an applicable '<domain> -' entry", CONF_FILE)); return PAM_SUCCESS; } if (retval != PAM_SUCCESS || pl->conf_file != NULL) /* skip reading limits.d if config file explicitely specified */ goto out; /* Read subsequent *.conf files, if they exist. */ /* set the LC_COLLATE so the sorting order doesn't depend on system locale */ oldlocale = setlocale(LC_COLLATE, "C"); glob_rc = glob(LIMITS_CONF_GLOB, GLOB_ERR, NULL, &globbuf); if (oldlocale != NULL) setlocale (LC_COLLATE, oldlocale); if (!glob_rc) { /* Parse the *.conf files. */ for (i = 0; globbuf.gl_pathv[i] != NULL; i++) { pl->conf_file = globbuf.gl_pathv[i]; retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl); if (retval == PAM_IGNORE) { D(("the configuration file ('%s') has an applicable '<domain> -' entry", pl->conf_file)); globfree(&globbuf); return PAM_SUCCESS; } if (retval != PAM_SUCCESS) goto out; } } out: globfree(&globbuf); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_WARNING, "error parsing the configuration file: '%s' ",CONF_FILE); return retval; } retval = setup_limits(pamh, pwd->pw_name, pwd->pw_uid, ctrl, pl); if (retval & LOGIN_ERR) pam_error(pamh, _("Too many logins for '%s'."), pwd->pw_name); if (retval != LIMITED_OK) { return PAM_PERM_DENIED; } return PAM_SUCCESS; }
static int _do_mail(pam_handle_t *pamh, int flags, int argc, const char **argv, int est) { int retval, ctrl, hashcount; char *path_mail=NULL, *folder; const char *type; /* * this module (un)sets the MAIL environment variable, and checks if * the user has any new mail. */ ctrl = _pam_parse(flags, argc, argv, &path_mail, &hashcount); /* Do we have anything to do? */ if (flags & PAM_SILENT) return PAM_SUCCESS; /* which folder? */ retval = get_folder(pamh, ctrl, &path_mail, &folder, hashcount); if (retval != PAM_SUCCESS) { D(("failed to find folder")); return retval; } /* set the MAIL variable? */ if (!(ctrl & PAM_NO_ENV) && est) { char *tmp; tmp = malloc(strlen(folder)+sizeof(MAIL_ENV_FORMAT)); if (tmp != NULL) { sprintf(tmp, MAIL_ENV_FORMAT, folder); D(("setting env: %s", tmp)); retval = pam_putenv(pamh, tmp); _pam_overwrite(tmp); _pam_drop(tmp); if (retval != PAM_SUCCESS) { _pam_overwrite(folder); _pam_drop(folder); _log_err(LOG_CRIT, "unable to set " MAIL_ENV_NAME " variable"); return retval; } } else { _log_err(LOG_CRIT, "no memory for " MAIL_ENV_NAME " variable"); _pam_overwrite(folder); _pam_drop(folder); return retval; } } else { D(("not setting " MAIL_ENV_NAME " variable")); } /* * OK. we've got the mail folder... what about its status? */ if ((est && !(ctrl & PAM_NO_LOGIN)) || (!est && (ctrl & PAM_LOGOUT_TOO))) { type = get_mail_status(ctrl, folder); if (type != NULL) { retval = report_mail(pamh, ctrl, type, folder); type = NULL; } } /* Delete environment variable? */ if (!est) (void) pam_putenv(pamh, MAIL_ENV_NAME); _pam_overwrite(folder); /* clean up */ _pam_drop(folder); /* indicate success or failure */ return retval; }
/* authorizes user on remote TACACS+ server, i.e. checks * his permission to access requested service * returns PAM_SUCCESS if the service is allowed */ PAM_EXTERN int pam_sm_acct_mgmt (pam_handle_t * pamh, int flags, int argc, const char **argv) { int retval, ctrl, status=PAM_AUTH_ERR; char *user; char *tty; char *r_addr; struct areply arep; struct tac_attrib *attr = NULL; int tac_fd; user = tty = r_addr = NULL; /* this also obtains service name for authorization this should be normally performed by pam_get_item(PAM_SERVICE) but since PAM service names are incompatible TACACS+ we have to pass it via command line argument until a better solution is found ;) */ ctrl = _pam_parse (argc, argv); if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)" , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); if ((user = _pam_get_user(pamh)) == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: username obtained [%s]", __FUNCTION__, user); tty = _pam_get_terminal(pamh); if(!strncmp(tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: tty obtained [%s]", __FUNCTION__, tty); r_addr = _pam_get_rhost(pamh); if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: rhost obtained [%s]", __FUNCTION__, r_addr); /* checks if user has been successfully authenticated by TACACS+; we cannot solely authorize user if it hasn't been authenticated or has been authenticated by method other than TACACS+ */ if(!active_server) { _pam_log (LOG_ERR, "user not authenticated by TACACS+"); return PAM_AUTH_ERR; } if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: active server is [%s]", __FUNCTION__, tac_ntop(active_server->ai_addr, active_server->ai_addrlen)); /* checks for specific data required by TACACS+, which should be supplied in command line */ if(tac_service == NULL || *tac_service == '\0') { _pam_log (LOG_ERR, "TACACS+ service type not configured"); return PAM_AUTH_ERR; } if(tac_protocol == NULL || *tac_protocol == '\0') { _pam_log (LOG_ERR, "TACACS+ protocol type not configured"); return PAM_AUTH_ERR; } tac_add_attrib(&attr, "service", tac_service); tac_add_attrib(&attr, "protocol", tac_protocol); tac_fd = tac_connect_single(active_server, active_key); if(tac_fd < 0) { _pam_log (LOG_ERR, "TACACS+ server unavailable"); if(arep.msg != NULL) free (arep.msg); close(tac_fd); return PAM_AUTH_ERR; } retval = tac_author_send(tac_fd, user, tty, r_addr, attr); tac_free_attrib(&attr); if(retval < 0) { _pam_log (LOG_ERR, "error getting authorization"); if(arep.msg != NULL) free (arep.msg); close(tac_fd); return PAM_AUTH_ERR; } if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: sent authorization request", __FUNCTION__); tac_author_read(tac_fd, &arep); if(arep.status != AUTHOR_STATUS_PASS_ADD && arep.status != AUTHOR_STATUS_PASS_REPL) { _pam_log (LOG_ERR, "TACACS+ authorisation failed for [%s]", user); if(arep.msg != NULL) free (arep.msg); close(tac_fd); return PAM_PERM_DENIED; } if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: user [%s] successfully authorized", __FUNCTION__, user); status = PAM_SUCCESS; attr = arep.attr; while (attr != NULL) { char attribute[attr->attr_len]; char value[attr->attr_len]; char *sep; sep = index(attr->attr, '='); if(sep == NULL) sep = index(attr->attr, '*'); if(sep != NULL) { bcopy(attr->attr, attribute, attr->attr_len-strlen(sep)); attribute[attr->attr_len-strlen(sep)] = '\0'; bcopy(sep, value, strlen(sep)); value[strlen(sep)] = '\0'; size_t i; for (i = 0; attribute[i] != '\0'; i++) { attribute[i] = toupper(attribute[i]); if (attribute[i] == '-') attribute[i] = '_'; } if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: returned attribute `%s%s' from server", __FUNCTION__, attribute, value); /* make returned attributes available for other PAM modules via PAM environment */ if (pam_putenv(pamh, strncat(attribute, value, strlen(value))) != PAM_SUCCESS) _pam_log(LOG_WARNING, "%s: unable to set PAM environment", __FUNCTION__); } else { _pam_log(LOG_WARNING, "%s: invalid attribute `%s', no separator", __FUNCTION__, attr->attr); } attr = attr->next; } /* free returned attributes */ if(arep.attr != NULL) tac_free_attrib(&arep.attr); if(arep.msg != NULL) free (arep.msg); close(tac_fd); return status; } /* pam_sm_acct_mgmt */
/* Tested for servers that require password change during challenge/response */ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, const char **argv) { int ctrl; char *user; char *pass; char *tty; char *r_addr; const void *pam_pass = NULL; int srv_i; int status; user = pass = tty = r_addr = NULL; ctrl = _pam_parse(argc, argv); if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u) (flags=%d, argc=%d)", __func__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT, flags, argc); if ( (pam_get_item(pamh, PAM_OLDAUTHTOK, &pam_pass) == PAM_SUCCESS) && (pam_pass != NULL) ) { if ((pass = strdup(pam_pass)) == NULL) return PAM_BUF_ERR; } else { pass = strdup(""); } _pam_get_user(pamh, &user); if (user == NULL) { if(pass) { free(pass); } return PAM_USER_UNKNOWN; } if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: user [%s] obtained", __func__, user); _pam_get_terminal(pamh, &tty); if (tty && !strncmp(tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: tty [%s] obtained", __func__, tty?tty:"UNKNOWN"); _pam_get_rhost(pamh, &r_addr); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: rhost [%s] obtained", __func__, r_addr?r_addr:"UNKNOWN"); if (PAM_SILENT != (flags & PAM_SILENT)) status = do_tac_connect(ctrl, NULL, user, pass, tty, r_addr, NULL, NULL, pamh); else status = PAM_AUTHTOK_ERR; if (status != PAM_SUCCESS && status != PAM_AUTHTOK_ERR) _pam_log(LOG_ERR, "no more servers to connect"); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: exit with pam status: %d", __func__, status); if (NULL != pass) { bzero(pass, strlen(pass)); free(pass); pass = NULL; } return status; } /* pam_sm_chauthtok */
int _pam_account(pam_handle_t *pamh, int argc, const char **argv, int type, char *cmd) { int retval; static int ctrl; char *user = NULL; char *tty = NULL; char *r_addr = NULL; char *typemsg; int status = PAM_SESSION_ERR; typemsg = tac_acct_flag2str(type); ctrl = _pam_parse (argc, argv); if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: [%s] called (pam_tacplus v%u.%u.%u)" , __FUNCTION__, typemsg, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: tac_srv_no=%d", __FUNCTION__, tac_srv_no); if ((user = _pam_get_user(pamh)) == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: username [%s] obtained", __FUNCTION__, user); tty = _pam_get_terminal(pamh); if(!strncmp(tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: tty [%s] obtained", __FUNCTION__, tty); r_addr = _pam_get_rhost(pamh); if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: rhost [%s] obtained", __FUNCTION__, r_addr); /* checks for specific data required by TACACS+, which should be supplied in command line */ if(tac_service == NULL || *tac_service == '\0') { _pam_log (LOG_ERR, "TACACS+ service type not configured"); return PAM_AUTH_ERR; } if(tac_protocol == NULL || *tac_protocol == '\0') { _pam_log (LOG_ERR, "TACACS+ protocol type not configured"); return PAM_AUTH_ERR; } /* when this module is called from within pppd or other application dealing with serial lines, it is likely that we will get hit with signal caused by modem hangup; this is important only for STOP packets, it's relatively rare that modem hangs up on accounting start */ if(type == TAC_PLUS_ACCT_FLAG_STOP) { signal(SIGALRM, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); } if(!(ctrl & PAM_TAC_ACCT)) { /* normal mode, send packet to the first available server */ int srv_i = 0; status = PAM_SESSION_ERR; while ((status == PAM_SESSION_ERR) && (srv_i < tac_srv_no)) { int tac_fd; tac_fd = tac_connect_single(tac_srv[srv_i], tac_srv_key[srv_i]); if(tac_fd < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (fd)", __FUNCTION__, typemsg); srv_i++; continue; } if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: connected with fd=%d (srv %d)", __FUNCTION__, tac_fd, srv_i); retval = _pam_send_account(tac_fd, type, user, tty, r_addr, cmd); /* return code from function in this mode is status of the last server we tried to send packet to */ if(retval < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (acct)", __FUNCTION__, typemsg); } else { status = PAM_SUCCESS; if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: [%s] for [%s] sent", __FUNCTION__, typemsg,user); } close(tac_fd); srv_i++; } } else { /* send packet to all servers specified */ int srv_i; status = PAM_SESSION_ERR; for(srv_i = 0; srv_i < tac_srv_no; srv_i++) { int tac_fd; tac_fd = tac_connect_single(tac_srv[srv_i], tac_srv_key[srv_i]); if(tac_fd < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (fd)", __FUNCTION__, typemsg); continue; } if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: connected with fd=%d (srv %d)", __FUNCTION__, tac_fd, srv_i); retval = _pam_send_account(tac_fd, type, user, tty, r_addr, cmd); /* return code from function in this mode is status of the last server we tried to send packet to */ if(retval < 0) { _pam_log(LOG_WARNING, "%s: error sending %s (acct)", __FUNCTION__, typemsg); } else { status = PAM_SUCCESS; if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_DEBUG, "%s: [%s] for [%s] sent", __FUNCTION__, typemsg,user); } close(tac_fd); } } /* acct mode */ if(type == TAC_PLUS_ACCT_FLAG_STOP) { signal(SIGALRM, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); } return status; }
/* authenticates user on remote TACACS+ server * returns PAM_SUCCESS if the supplied username and password * pair is valid */ PAM_EXTERN int pam_sm_authenticate (pam_handle_t * pamh, int flags, int argc, const char **argv) { int ctrl, retval; char *user; char *pass; char *tty; char *r_addr; int srv_i; int tac_fd; int status = PAM_AUTH_ERR; int seq = 0; user = pass = tty = r_addr = NULL; ctrl = _pam_parse (argc, argv); if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)" , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); if ((user = _pam_get_user(pamh)) == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: user [%s] obtained", __FUNCTION__, user); /* uwzgledniac PAM_DISALLOW_NULL_AUTHTOK */ retval = tacacs_get_password (pamh, flags, ctrl, &pass); if (retval != PAM_SUCCESS || pass == NULL || *pass == '\0') { _pam_log (LOG_ERR, "unable to obtain password"); return PAM_CRED_INSUFFICIENT; } retval = pam_set_item (pamh, PAM_AUTHTOK, pass); if (retval != PAM_SUCCESS) { _pam_log (LOG_ERR, "unable to set password"); return PAM_CRED_INSUFFICIENT; } if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: password obtained", __FUNCTION__); tty = _pam_get_terminal(pamh); if (!strncmp (tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tty [%s] obtained", __FUNCTION__, tty); r_addr = _pam_get_rhost(pamh); if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: rhost [%s] obtained", __FUNCTION__, r_addr); /* Attempt server connect */ for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { status = TAC_PLUS_AUTHEN_STATUS_FAIL; if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i ); tac_fd = tac_connect_single(tac_srv[srv_i], tac_srv_key[srv_i]); if (tac_fd < 0) { _pam_log (LOG_ERR, "connection failed srv %d: %m", srv_i); if (srv_i == tac_srv_no-1) { _pam_log (LOG_ERR, "no more servers to connect"); return PAM_AUTHINFO_UNAVAIL; } continue; } /* Send AUTHEN/START */ if (tac_authen_send(tac_fd, user, pass, tty, r_addr, TAC_PLUS_AUTHEN_LOGIN, ctrl) < 0) { _pam_log (LOG_ERR, "error sending auth req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } else { /* Read AUTHEN/REPLY and act on status */ struct msg_status *msgstatus = malloc(sizeof(msg_status)); do { tac_authen_read(msgstatus, tac_fd, ctrl, &seq); status = msgstatus->status; switch (status) { case TAC_PLUS_AUTHEN_STATUS_GETPASS: /* AUTHEN/CONT with password */ if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, pass, ctrl, seq+1) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_MAXTRIES; } break; case TAC_PLUS_AUTHEN_STATUS_GETDATA: { /* The only GETDATA request should be if the user's password * has expired and ACS is requesting a new password * * Check if a conversation function has been set and either * return a PAM_AUTHTOK_EXPIRED or start a conversation * with the user for to change the password */ if (!strcmp(tty,"http")) { status = PAM_NEW_AUTHTOK_REQD; if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: expired", __FUNCTION__); } else { /* Get data from user with pam conversation */ struct pam_message msg; struct pam_response *resp = NULL; int retval; char *user_data = NULL; /* set up conversation call */ msg.msg_style = PAM_PROMPT_ECHO_OFF; msg.msg = malloc(100); strcpy((char *)msg.msg,msgstatus->server_msg); if ((retval = converse (pamh, 1, &msg, &resp)) != PAM_SUCCESS) { status = PAM_AUTHINFO_UNAVAIL; } else { if (resp != NULL) { if (resp->resp == NULL && (ctrl & PAM_TAC_DEBUG)) _pam_log (LOG_DEBUG, "pam_sm_authenticate: NULL given by user for GETDATA request"); user_data = resp->resp; resp->resp = NULL; } else { if (ctrl & PAM_TAC_DEBUG) { _pam_log (LOG_DEBUG, "pam_sm_authenticate: no error reported"); _pam_log (LOG_DEBUG, "getting data from user - NULL returned!?"); } return PAM_CONV_ERR; } /* AUTHEN/CONT with data */ if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, user_data, ctrl, seq+1) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } } free(msg.msg); } } break; case TAC_PLUS_AUTHEN_STATUS_GETUSER: /* AUTHEN/CONT with username */ if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, user, ctrl, seq+1) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } break; } } while ( (status == TAC_PLUS_AUTHEN_STATUS_GETDATA) || (status == TAC_PLUS_AUTHEN_STATUS_GETPASS) || (status == TAC_PLUS_AUTHEN_STATUS_GETUSER) ); if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: out of while loop status=%d", __FUNCTION__,status); if (status == TAC_PLUS_AUTHEN_STATUS_PASS) { /* OK, we got authenticated; save the server that accepted us for pam_sm_acct_mgmt and exit the loop */ status = PAM_SUCCESS; active_server = tac_srv[srv_i]; active_key = tac_srv_key[srv_i]; } else if (status != PAM_NEW_AUTHTOK_REQD) { _pam_log (LOG_ERR, "auth failed: %d", status); status = PAM_AUTH_ERR; } } close(tac_fd); /* TODO: Allow time for tac server to reply * TODO: Check if reply received before connecting to next server */ } if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: exit with pam status: %i", __FUNCTION__, status); bzero (pass, strlen (pass)); free(pass); pass = NULL; return status; } /* pam_sm_authenticate */
/* authenticates user on remote TACACS+ server * returns PAM_SUCCESS if the supplied username and password * pair is valid */ PAM_EXTERN int pam_sm_authenticate (pam_handle_t * pamh, int flags, int argc, const char **argv) { int ctrl, retval; char *user; char *pass; char *tty; char *r_addr; int srv_i; int tac_fd, status, msg, communicating; user = pass = tty = r_addr = NULL; ctrl = _pam_parse(argc, argv); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)", __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); if ((user = _pam_get_user(pamh)) == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: user [%s] obtained", __FUNCTION__, user); /* uwzgledniac PAM_DISALLOW_NULL_AUTHTOK */ retval = tacacs_get_password (pamh, flags, ctrl, &pass); if (retval != PAM_SUCCESS || pass == NULL || *pass == '\0') { _pam_log(LOG_ERR, "unable to obtain password"); return PAM_CRED_INSUFFICIENT; } retval = pam_set_item (pamh, PAM_AUTHTOK, pass); if (retval != PAM_SUCCESS) { _pam_log(LOG_ERR, "unable to set password"); return PAM_CRED_INSUFFICIENT; } if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: password obtained", __FUNCTION__); tty = _pam_get_terminal(pamh); if (!strncmp(tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: tty [%s] obtained", __FUNCTION__, tty); r_addr = _pam_get_rhost(pamh); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: rhost [%s] obtained", __FUNCTION__, r_addr); status = PAM_AUTHINFO_UNAVAIL; for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i ); tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key); if (tac_fd < 0) { _pam_log(LOG_ERR, "connection failed srv %d: %m", srv_i); continue; } if (tac_authen_send(tac_fd, user, pass, tty, r_addr) < 0) { close(tac_fd); _pam_log(LOG_ERR, "error sending auth req to TACACS+ server"); continue; } communicating = 1; while (communicating) { msg = tac_authen_read(tac_fd); /* talk the protocol */ switch (msg) { case TAC_PLUS_AUTHEN_STATUS_PASS: /* success */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_PASS"); status = PAM_SUCCESS; communicating = 0; active_server.addr = tac_srv[srv_i].addr; active_server.key = tac_srv[srv_i].key; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: active srv %d", __FUNCTION__, srv_i); break; case TAC_PLUS_AUTHEN_STATUS_FAIL: /* forget it */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_FAIL"); status = PAM_AUTH_ERR; communicating = 0; _pam_log(LOG_ERR, "auth failed: %d", msg); break; case TAC_PLUS_AUTHEN_STATUS_GETDATA: /* not implemented */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETDATA"); communicating = 0; break; case TAC_PLUS_AUTHEN_STATUS_GETUSER: /* not implemented */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETUSER"); communicating = 0; break; case TAC_PLUS_AUTHEN_STATUS_GETPASS: if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETPASS"); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, pass) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); communicating = 0; break; } /* continue the while loop; go read tac response */ break; case TAC_PLUS_AUTHEN_STATUS_RESTART: /* try it again */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_RESTART"); /* * not implemented * WdJ: I *think* you can just do tac_authen_send(user, pass) again * but I'm not sure */ communicating = 0; break; case TAC_PLUS_AUTHEN_STATUS_ERROR: /* server has problems */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_ERROR"); communicating = 0; break; case TAC_PLUS_AUTHEN_STATUS_FOLLOW: /* server tells to try a different server address */ /* not implemented */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_FOLLOW"); communicating = 0; break; default: if (msg < 0) { /* connection error */ communicating = 0; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "error communicating with tacacs server"); break; } /* unknown response code */ communicating = 0; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: unknown response 0x%02x", msg); } } /* end while(communicating) */ close(tac_fd); if (status == PAM_SUCCESS || status == PAM_AUTH_ERR) break; } if (status != PAM_SUCCESS && status != PAM_AUTH_ERR) _pam_log(LOG_ERR, "no more servers to connect"); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: exit with pam status: %d", __FUNCTION__, status); bzero(pass, strlen (pass)); free(pass); pass = NULL; return status; } /* pam_sm_authenticate */
int pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv) { int retval, anon=0, ctrl; const char *user; const char *users = NULL; /* * this module checks if the user name is ftp or annonymous. If * this is the case, it can set the PAM_RUSER to the entered email * address and SUCCEEDS, otherwise it FAILS. */ ctrl = _pam_parse(pamh, argc, argv, &users); retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS || user == NULL) { pam_syslog(pamh, LOG_ERR, "no user specified"); return PAM_USER_UNKNOWN; } if (!(ctrl & PAM_NO_ANON)) { anon = lookup(user, users, &user); } if (anon) { retval = pam_set_item(pamh, PAM_USER, (const void *)user); if (retval != PAM_SUCCESS || user == NULL) { pam_syslog(pamh, LOG_ERR, "user resetting failed"); return PAM_USER_UNKNOWN; } } /* * OK. we require an email address for user or the user's password. * - build conversation and get their input. */ { char *resp = NULL; const char *token; if (!anon) retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp, PLEASE_ENTER_PASSWORD, user); else retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp, GUEST_LOGIN_PROMPT); if (retval != PAM_SUCCESS) { _pam_overwrite (resp); _pam_drop (resp); return ((retval == PAM_CONV_AGAIN) ? PAM_INCOMPLETE:PAM_AUTHINFO_UNAVAIL); } if (anon) { /* XXX: Some effort should be made to verify this email address! */ if (!(ctrl & PAM_IGNORE_EMAIL)) { char *sptr = NULL; token = strtok_r(resp, "@", &sptr); retval = pam_set_item(pamh, PAM_RUSER, token); if ((token) && (retval == PAM_SUCCESS)) { token = strtok_r(NULL, "@", &sptr); retval = pam_set_item(pamh, PAM_RHOST, token); } } /* we are happy to grant annonymous access to the user */ retval = PAM_SUCCESS; } else { /* * we have a password so set AUTHTOK */ pam_set_item(pamh, PAM_AUTHTOK, resp); /* * this module failed, but the next one might succeed with * this password. */ retval = PAM_AUTH_ERR; } /* clean up */ _pam_overwrite(resp); _pam_drop(resp); /* success or failure */ return retval; } }
/* authenticates user on remote TACACS+ server * returns PAM_SUCCESS if the supplied username and password * pair is valid */ PAM_EXTERN int pam_sm_authenticate (pam_handle_t * pamh, int flags, int argc, const char **argv) { int ctrl, retval; char *user; char *pass; char *tty; char *r_addr; int srv_i; int tac_fd; int status = PAM_AUTH_ERR; user = pass = tty = r_addr = NULL; ctrl = _pam_parse (argc, argv); if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)" , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); if ((user = _pam_get_user(pamh)) == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: user [%s] obtained", __FUNCTION__, user); /* uwzgledniac PAM_DISALLOW_NULL_AUTHTOK */ retval = tacacs_get_password (pamh, flags, ctrl, &pass); if (retval != PAM_SUCCESS || pass == NULL || *pass == '\0') { _pam_log (LOG_ERR, "unable to obtain password"); return PAM_CRED_INSUFFICIENT; } retval = pam_set_item (pamh, PAM_AUTHTOK, pass); if (retval != PAM_SUCCESS) { _pam_log (LOG_ERR, "unable to set password"); return PAM_CRED_INSUFFICIENT; } if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: password obtained", __FUNCTION__); tty = _pam_get_terminal(pamh); if (!strncmp (tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: tty [%s] obtained", __FUNCTION__, tty); r_addr = _pam_get_rhost(pamh); if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: rhost [%s] obtained", __FUNCTION__, r_addr); for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { int msg = TAC_PLUS_AUTHEN_STATUS_FAIL; if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i ); tac_fd = tac_connect_single(tac_srv[srv_i], tac_srv_key[srv_i]); if (tac_fd < 0) { _pam_log (LOG_ERR, "connection failed srv %d: %m", srv_i); if (srv_i == tac_srv_no-1) { _pam_log (LOG_ERR, "no more servers to connect"); return PAM_AUTHINFO_UNAVAIL; } continue; } if (tac_authen_send(tac_fd, user, pass, tty, r_addr) < 0) { _pam_log (LOG_ERR, "error sending auth req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } else { msg = tac_authen_read(tac_fd); if (msg == TAC_PLUS_AUTHEN_STATUS_GETPASS) { if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, pass) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } else { msg = tac_authen_read(tac_fd); if (msg != TAC_PLUS_AUTHEN_STATUS_PASS) { _pam_log (LOG_ERR, "auth failed: %d", msg); status = PAM_AUTH_ERR; } else { /* OK, we got authenticated; save the server that accepted us for pam_sm_acct_mgmt and exit the loop */ status = PAM_SUCCESS; active_server = tac_srv[srv_i]; active_key = tac_srv_key[srv_i]; close(tac_fd); break; } } } else if (msg != TAC_PLUS_AUTHEN_STATUS_PASS) { _pam_log (LOG_ERR, "auth failed: %d", msg); status = PAM_AUTH_ERR; } else { /* OK, we got authenticated; save the server that accepted us for pam_sm_acct_mgmt and exit the loop */ status = PAM_SUCCESS; active_server = tac_srv[srv_i]; active_key = tac_srv_key[srv_i]; close(tac_fd); break; } } close(tac_fd); } if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: exit with pam status: %i", __FUNCTION__, status); bzero (pass, strlen (pass)); free(pass); pass = NULL; return status; } /* pam_sm_authenticate */
/* authenticates user on remote TACACS+ server * returns PAM_SUCCESS if the supplied username and password * pair is valid */ PAM_EXTERN int pam_sm_authenticate (pam_handle_t * pamh, int flags, int argc, const char **argv) { int ctrl, retval; char *user, *puser; char *pass; char *tty; char *r_addr; int status; priv_level = 0; user = pass = tty = r_addr = NULL; ctrl = _pam_parse(argc, argv); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)", __func__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); /* reset static state in case we are re-entered */ _reset_saved_user(ctrl & PAM_TAC_DEBUG); /* * If a mapped user entry already exists, we are probably being * used for su or sudo, so we need to get the original user password, * rather than the mapped user. * Decided based on auid != uid and then do the lookup, similar to * find_pw_user() in nss_tacplusc */ _pam_get_user(pamh, &puser); user = get_user_to_auth(puser); if (user == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: user [%s] obtained", __func__, user); retval = tacacs_get_password (pamh, flags, ctrl, &pass); if (retval != PAM_SUCCESS || pass == NULL || *pass == '\0') { _pam_log(LOG_ERR, "unable to obtain password"); status = PAM_CRED_INSUFFICIENT; goto err; } retval = pam_set_item (pamh, PAM_AUTHTOK, pass); if (retval != PAM_SUCCESS) { _pam_log(LOG_ERR, "unable to set password"); status = PAM_CRED_INSUFFICIENT; goto err; } if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: password obtained", __func__); _pam_get_terminal(pamh, &tty); if (!strncmp(tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: tty [%s] obtained", __func__, tty); _pam_get_rhost(pamh, &r_addr); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: rhost [%s] obtained", __func__, r_addr); status = do_tac_connect(ctrl, NULL, user, pass, tty, r_addr, NULL, NULL, pamh); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: exit with pam status: %d", __func__, status); err: if (user && user != puser) free(user); /* it was stdrup'ed */ if (NULL != pass) { bzero(pass, strlen (pass)); free(pass); } return status; } /* pam_sm_authenticate */
PAM_EXTERN int pam_sm_chauthtok (pam_handle_t * pamh, int flags, int argc, const char **argv) { int ctrl, retval; char *user; char *pass; char *tty; char *r_addr; int srv_i; int tac_fd; int status = PAM_TRY_AGAIN; int seq = 0; user = pass = tty = r_addr = NULL; ctrl = _pam_parse (argc, argv); if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)" , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); /* Preliminary call to check readiness of our module */ if (flags & PAM_PRELIM_CHECK) { /* Can we connect to TACACS? */ for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i ); tac_fd = tac_connect_single(tac_srv[srv_i], tac_srv_key[srv_i]); if (tac_fd < 0) { _pam_log (LOG_ERR, "connection failed srv %d: %m", srv_i); if (srv_i == tac_srv_no-1) { _pam_log (LOG_ERR, "no more servers to connect"); return PAM_TRY_AGAIN; } continue; } } close(tac_fd); return PAM_SUCCESS; } if ((user = _pam_get_user(pamh)) == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: user [%s] obtained", __FUNCTION__, user); /* CHPASS does not send a password in data field, * user is prompted by a GETDATA reply packet */ pass = malloc(5); strcpy(pass,"null"); tty = _pam_get_terminal(pamh); if (!strncmp (tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tty [%s] obtained", __FUNCTION__, tty); r_addr = _pam_get_rhost(pamh); if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: rhost [%s] obtained", __FUNCTION__, r_addr); /* Attempt server connect */ for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { status = TAC_PLUS_AUTHEN_STATUS_FAIL; if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i ); tac_fd = tac_connect_single(tac_srv[srv_i], tac_srv_key[srv_i]); if (tac_fd < 0) { _pam_log (LOG_ERR, "connection failed srv %d: %m", srv_i); if (srv_i == tac_srv_no-1) { _pam_log (LOG_ERR, "no more servers to connect"); return PAM_AUTHINFO_UNAVAIL; } continue; } /* Send AUTHEN/START */ if (tac_authen_send(tac_fd, user, pass, tty, r_addr, TAC_PLUS_AUTHEN_CHPASS, ctrl) < 0) { _pam_log (LOG_ERR, "error sending auth req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } else { /* Read AUTHEN/REPLY and act on status */ struct msg_status *msgstatus = malloc(sizeof(msg_status)); do { tac_authen_read(msgstatus, tac_fd, ctrl, &seq); status = msgstatus->status; switch (status) { case TAC_PLUS_AUTHEN_STATUS_GETPASS: /* AUTHEN/CONT with password */ if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, pass, ctrl, seq+1) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } break; case TAC_PLUS_AUTHEN_STATUS_GETDATA: { /* Get data from user with pam conversation */ struct pam_message msg; struct pam_response *resp = NULL; int retval; char *user_data = NULL; /* set up conversation call */ msg.msg_style = PAM_PROMPT_ECHO_OFF; msg.msg = malloc(100); strcpy((char *)msg.msg,msgstatus->server_msg); if ((retval = converse (pamh, 1, &msg, &resp)) != PAM_SUCCESS) { status = PAM_AUTHINFO_UNAVAIL; } else { if (resp != NULL) { if (resp->resp == NULL && (ctrl & PAM_TAC_DEBUG)) _pam_log (LOG_DEBUG, "pam_sm_authenticate: NULL given by user for GETDATA request"); user_data = resp->resp; resp->resp = NULL; } else { if (ctrl & PAM_TAC_DEBUG) { _pam_log (LOG_DEBUG, "pam_sm_authenticate: no error reported"); _pam_log (LOG_DEBUG, "getting data from user - NULL returned!?"); } return PAM_CONV_ERR; } /* AUTHEN/CONT with data */ if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, user_data, ctrl, seq+1) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } } free(msg.msg); } break; case TAC_PLUS_AUTHEN_STATUS_GETUSER: /* AUTHEN/CONT with username */ if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, user, ctrl, seq+1) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_AUTHINFO_UNAVAIL; } break; } } while ( (status == TAC_PLUS_AUTHEN_STATUS_GETDATA) || (status == TAC_PLUS_AUTHEN_STATUS_GETPASS) || (status == TAC_PLUS_AUTHEN_STATUS_GETUSER) ); if (status != TAC_PLUS_AUTHEN_STATUS_PASS) { _pam_log (LOG_ERR, "auth failed: %d", status); status = PAM_AUTHTOK_ERR; } else { /* OK, we got authenticated; save the server that accepted us for pam_sm_acct_mgmt and exit the loop */ status = PAM_SUCCESS; active_server = tac_srv[srv_i]; active_key = tac_srv_key[srv_i]; close(tac_fd); break; } } close(tac_fd); } if (ctrl & PAM_TAC_DEBUG) _pam_log (LOG_DEBUG, "%s: exit with pam status: %i", __FUNCTION__, status); bzero (pass, strlen (pass)); free(pass); pass = NULL; return status; } /* pam_sm_chauthtok */
/* authorizes user on remote TACACS+ server, i.e. checks * his permission to access requested service * returns PAM_SUCCESS if the service is allowed */ PAM_EXTERN int pam_sm_acct_mgmt (pam_handle_t * pamh, int flags, int argc, const char **argv) { int ctrl, status=PAM_AUTH_ERR; char *user; char *tty; char *r_addr; struct areply arep; struct tac_attrib *attr_s = NULL, *attr; int tac_fd = -1; user = tty = r_addr = NULL; memset(&arep, 0, sizeof(arep)); /* this also obtains service name for authorization this should be normally performed by pam_get_item(PAM_SERVICE) but since PAM service names are incompatible TACACS+ we have to pass it via command line argument until a better solution is found ;) */ ctrl = _pam_parse (argc, argv); if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)", __func__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); _pam_get_user(pamh, &user); if (user == NULL) return PAM_USER_UNKNOWN; _pam_get_terminal(pamh, &tty); if(!strncmp(tty, "/dev/", 5)) tty += 5; _pam_get_rhost(pamh, &r_addr); /* checks for specific data required by TACACS+, which should be supplied in pam module command line */ if(!*tac_service) { _pam_log (LOG_ERR, "TACACS+ service type not configured"); return PAM_AUTH_ERR; } tac_add_attrib(&attr_s, "service", tac_service); if(tac_protocol != NULL && tac_protocol[0] != '\0') tac_add_attrib(&attr_s, "protocol", tac_protocol); else _pam_log (LOG_ERR, "TACACS+ protocol type not configured (IGNORED)"); tac_add_attrib(&attr_s, "cmd", ""); memset(&arep, 0, sizeof arep); /* * Check if user is authorized, independently of authentication. * Authentication may have happened via ssh public key, rather than * via TACACS+. PAM should not normally get to this entry point if * user is not yet authenticated. * We only write the mapping entry (if needed) when authorization * is succesful. * attr is not used here, but having a non-NULL value is how * talk_tac_server() distinguishes that it is an acct call, vs auth * TODO: use a different mechanism */ status = do_tac_connect(ctrl, &tac_fd, user, NULL, tty, r_addr, &attr_s, &arep, pamh); tac_free_attrib(&attr_s); if(active_server.addr == NULL || tac_fd < 0) { /* we need to return PAM_AUTHINFO_UNAVAIL here, rather than * PAM_AUTH_ERR, or we can't use "ignore" in the pam configuration */ status = PAM_AUTHINFO_UNAVAIL; goto cleanup; } if(status) { if (ctrl & PAM_TAC_DEBUG) _pam_log(LOG_NOTICE, "No TACACS mapping for %s after auth failure", user); goto cleanup; } if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: user [%s] successfully authorized", __func__, user); attr = arep.attr; while (attr != NULL) { char attribute[attr->attr_len]; char value[attr->attr_len]; char attrenv[attr->attr_len]; char *sep; sep = index(attr->attr, '='); if(sep == NULL) sep = index(attr->attr, '*'); if(sep != NULL) { bcopy(attr->attr, attribute, attr->attr_len-strlen(sep)); attribute[attr->attr_len-strlen(sep)] = '\0'; bcopy(sep, value, strlen(sep)); value[strlen(sep)] = '\0'; size_t i; for (i = 0; attribute[i] != '\0'; i++) { attribute[i] = toupper(attribute[i]); if (attribute[i] == '-') attribute[i] = '_'; } if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: returned attribute `%s(%s)' from server", __func__, attribute, value); if(strncmp(attribute, "PRIV", 4) == 0) { char *ok; /* because of the separation above, value will start with * the separator, which we don't want. */ priv_level = (unsigned)strtoul(value+1, &ok, 0); /* if this fails, we leave priv_level at 0, which is * least privileged, so that's OK, but at least report it */ if (ok == value) _pam_log (LOG_WARNING, "%s: non-numeric privilege for %s, got (%s)", __func__, attribute, value+1); } /* * make returned attributes available for other PAM modules via PAM * environment. Since separator can be = or *, ensure it's = for * the env. */ snprintf(attrenv, sizeof attribute, "%s=%s", attribute, value+1); if (pam_putenv(pamh, attrenv) != PAM_SUCCESS) _pam_log(LOG_WARNING, "%s: unable to set PAM environment (%s)", __func__, attribute); } else { syslog(LOG_WARNING, "%s: invalid attribute `%s', no separator", __func__, attr->attr); } attr = attr->next; } update_mapped(pamh, user, priv_level, r_addr); cleanup: /* free returned attributes */ if(arep.attr != NULL) tac_free_attrib(&arep.attr); if(arep.msg != NULL) free (arep.msg); if(tac_fd >= 0) { active_server.addr = NULL; close(tac_fd); tac_fd = -1; } return status; } /* pam_sm_acct_mgmt */
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { const char *username; const char *password; const char *member = NULL; int retval = PAM_AUTH_ERR; int i; /* parse arguments */ int ctrl = _pam_parse(argc, argv); /* Get the username */ retval = pam_get_user(pamh, &username, NULL); if ((retval != PAM_SUCCESS) || (!username)) { if (ctrl & WINBIND_DEBUG_ARG) _pam_log(LOG_DEBUG,"can not get the username"); return PAM_SERVICE_ERR; } retval = _winbind_read_password(pamh, ctrl, NULL, "Password: "******"Could not retrieve user's password"); return PAM_AUTHTOK_ERR; } if (ctrl & WINBIND_DEBUG_ARG) { /* Let's not give too much away in the log file */ #ifdef DEBUG_PASSWORD _pam_log(LOG_INFO, "Verify user `%s' with password `%s'", username, password); #else _pam_log(LOG_INFO, "Verify user `%s'", username); #endif } /* Retrieve membership-string here */ for ( i=0; i<argc; i++ ) { if ((strncmp(argv[i], "require_membership_of", strlen("require_membership_of")) == 0) || (strncmp(argv[i], "require-membership-of", strlen("require-membership-of")) == 0)) { char *p; char *parm = strdup(argv[i]); if ( (p = strchr( parm, '=' )) == NULL) { _pam_log(LOG_INFO, "no \"=\" delimiter for \"require_membership_of\" found\n"); break; } member = strdup(p+1); } } /* Now use the username to look up password */ return winbind_auth_request(username, password, member, ctrl); }
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc ,const char **argv) { int retval, anon=0, ctrl; const char *user; char *users=NULL; /* * this module checks if the user name is ftp or annonymous. If * this is the case, it can set the PAM_RUSER to the entered email * address and SUCCEEDS, otherwise it FAILS. */ ctrl = _pam_parse(argc, argv, &users); retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS || user == NULL) { _pam_log(LOG_ERR, "no user specified"); return PAM_USER_UNKNOWN; } if (!(ctrl & PAM_NO_ANON)) { anon = lookup(user, users, &user); } if (anon) { retval = pam_set_item(pamh, PAM_USER, (const void *)user); if (retval != PAM_SUCCESS || user == NULL) { _pam_log(LOG_ERR, "user resetting failed"); return PAM_USER_UNKNOWN; } } /* * OK. we require an email address for user or the user's password. * - build conversation and get their input. */ { struct pam_message msg[1], *mesg[1]; struct pam_response *resp=NULL; const char *token; char *prompt=NULL; int i=0; if (!anon) { prompt = malloc(strlen(PLEASE_ENTER_PASSWORD) + strlen(user)); if (prompt == NULL) { D(("out of memory!?")); return PAM_BUF_ERR; } else { sprintf(prompt, PLEASE_ENTER_PASSWORD, user); msg[i].msg = prompt; } } else { msg[i].msg = GUEST_LOGIN_PROMPT; } msg[i].msg_style = PAM_PROMPT_ECHO_OFF; mesg[i] = &msg[i]; retval = converse(pamh, ++i, mesg, &resp); if (prompt) { _pam_overwrite(prompt); _pam_drop(prompt); } if (retval != PAM_SUCCESS) { if (resp != NULL) _pam_drop_reply(resp,i); return ((retval == PAM_CONV_AGAIN) ? PAM_INCOMPLETE:PAM_AUTHINFO_UNAVAIL); } if (anon) { /* XXX: Some effort should be made to verify this email address! */ if (!(ctrl & PAM_IGNORE_EMAIL)) { token = strtok(resp->resp, "@"); retval = pam_set_item(pamh, PAM_RUSER, token); if ((token) && (retval == PAM_SUCCESS)) { token = strtok(NULL, "@"); retval = pam_set_item(pamh, PAM_RHOST, token); } } /* we are happy to grant annonymous access to the user */ retval = PAM_SUCCESS; } else { /* * we have a password so set AUTHTOK */ (void) pam_set_item(pamh, PAM_AUTHTOK, resp->resp); /* * this module failed, but the next one might succeed with * this password. */ retval = PAM_AUTH_ERR; } if (resp) { /* clean up */ _pam_drop_reply(resp, i); } /* success or failure */ return retval; } }
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, const char **argv) { unsigned int lctrl; int retval; unsigned int ctrl = _pam_parse(argc, argv); /* <DO NOT free() THESE> */ const char *user; const char *member = NULL; char *pass_old, *pass_new; /* </DO NOT free() THESE> */ char *Announce; int retry = 0; /* * First get the name of a user */ retval = pam_get_user(pamh, &user, "Username: "******"username was NULL!"); return PAM_USER_UNKNOWN; } if (retval == PAM_SUCCESS && on(WINBIND_DEBUG_ARG, ctrl)) _pam_log(LOG_DEBUG, "username [%s] obtained", user); } else { if (on(WINBIND_DEBUG_ARG, ctrl)) _pam_log(LOG_DEBUG, "password - could not identify user"); return retval; } /* * obtain and verify the current password (OLDAUTHTOK) for * the user. */ if (flags & PAM_PRELIM_CHECK) { /* instruct user what is happening */ #define greeting "Changing password for " Announce = (char *) malloc(sizeof(greeting) + strlen(user)); if (Announce == NULL) { _pam_log(LOG_CRIT, "password - out of memory"); return PAM_BUF_ERR; } (void) strcpy(Announce, greeting); (void) strcpy(Announce + sizeof(greeting) - 1, user); #undef greeting lctrl = ctrl | WINBIND__OLD_PASSWORD; retval = _winbind_read_password(pamh, lctrl ,Announce ,"(current) NT password: "******"password - (old) token not obtained"); return retval; } /* verify that this is the password for this user */ retval = winbind_auth_request(user, pass_old, member, ctrl); if (retval != PAM_ACCT_EXPIRED && retval != PAM_AUTHTOK_EXPIRED && retval != PAM_NEW_AUTHTOK_REQD && retval != PAM_SUCCESS) { pass_old = NULL; return retval; } retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old); pass_old = NULL; if (retval != PAM_SUCCESS) { _pam_log(LOG_CRIT, "failed to set PAM_OLDAUTHTOK"); } } else if (flags & PAM_UPDATE_AUTHTOK) { /* * obtain the proposed password */ /* * get the old token back. */ retval = pam_get_item(pamh, PAM_OLDAUTHTOK ,(const void **) &pass_old); if (retval != PAM_SUCCESS) { _pam_log(LOG_NOTICE, "user not authenticated"); return retval; } lctrl = ctrl; if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) { lctrl |= WINBIND_USE_FIRST_PASS_ARG; } retry = 0; retval = PAM_AUTHTOK_ERR; while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) { /* * use_authtok is to force the use of a previously entered * password -- needed for pluggable password strength checking */ retval = _winbind_read_password(pamh, lctrl ,NULL ,"Enter new NT password: "******"Retype new NT password: "******"password - new password not obtained"); } pass_old = NULL;/* tidy up */ return retval; } /* * At this point we know who the user is and what they * propose as their new password. Verify that the new * password is acceptable. */ if (pass_new[0] == '\0') {/* "\0" password = NULL */ pass_new = NULL; } } /* * By reaching here we have approved the passwords and must now * rebuild the password database file. */ retval = winbind_chauthtok_request(user, pass_old, pass_new, ctrl); _pam_overwrite(pass_new); _pam_overwrite(pass_old); pass_old = pass_new = NULL; } else { retval = PAM_SERVICE_ERR; } return retval; }
/* now the session stuff */ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { int retval; char *user_name; struct passwd *pwd; int ctrl; struct pam_limit_s pl; D(("called.")); memset(&pl, 0, sizeof(pl)); ctrl = _pam_parse(argc, argv, &pl); retval = pam_get_item( pamh, PAM_USER, (void*) &user_name ); if ( user_name == NULL || retval != PAM_SUCCESS ) { _pam_log(LOG_CRIT, "open_session - error recovering username"); return PAM_SESSION_ERR; } setpwent(); pwd = getpwnam(user_name); endpwent(); if (!pwd) { if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_WARNING, "open_session username '%s' does not exist", user_name); return PAM_SESSION_ERR; } /* do not impose limits on UID 0 accounts */ if (!pwd->pw_uid) { if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_DEBUG, "user '%s' have UID 0 - no limits imposed", user_name); return PAM_SUCCESS; } retval = init_limits(&pl); if (retval != PAM_SUCCESS) { _pam_log(LOG_WARNING, "cannot initialize"); return PAM_IGNORE; } retval = parse_config_file(pwd->pw_name, ctrl, &pl); if (retval == PAM_IGNORE) { D(("the configuration file has an applicable '<domain> -' entry")); return PAM_SUCCESS; } if (retval != PAM_SUCCESS) { _pam_log(LOG_WARNING, "error parsing the configuration file"); return PAM_IGNORE; } if (ctrl & PAM_DO_SETREUID) setreuid(pwd->pw_uid, -1); retval = setup_limits(pwd->pw_name, ctrl, &pl); if (retval & LOGIN_ERR) { printf("\nToo many logins for '%s'\n",pwd->pw_name); sleep(2); return PAM_PERM_DENIED; } return PAM_SUCCESS; }
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) { int ctrl; struct module_options options; memset(&options, 0, sizeof(options)); options.retry_times = CO_RETRY_TIMES; ctrl = _pam_parse(pamh, &options, argc, argv); if (ctrl < 0) return PAM_BUF_ERR; if (flags & PAM_PRELIM_CHECK) { /* Check for passwd dictionary * We cannot do that, since the original path is compiled * into the cracklib library and we don't know it. */ return PAM_SUCCESS; } else if (flags & PAM_UPDATE_AUTHTOK) { int retval; const void *oldtoken; const char *user; int tries; retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS || user == NULL) { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_ERR, "Can not get username"); return PAM_AUTHTOK_ERR; } retval = pam_get_item(pamh, PAM_OLDAUTHTOK, &oldtoken); if (retval != PAM_SUCCESS) { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_ERR, "Can not get old passwd"); oldtoken = NULL; } tries = 0; while (tries < options.retry_times) { void *auxerror; const char *newtoken = NULL; tries++; /* Planned modus operandi: * Get a passwd. * Verify it against libpwquality. * If okay get it a second time. * Check to be the same with the first one. * set PAM_AUTHTOK and return */ retval = pam_get_authtok_noverify(pamh, &newtoken, NULL); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "pam_get_authtok_noverify returned error: %s", pam_strerror(pamh, retval)); continue; } else if (newtoken == NULL) { /* user aborted password change, quit */ return PAM_AUTHTOK_ERR; } /* now test this passwd against libpwquality */ retval = pwquality_check(options.pwq, newtoken, oldtoken, user, &auxerror); if (retval < 0) { const char *msg; char buf[PWQ_MAX_ERROR_MESSAGE_LEN]; msg = pwquality_strerror(buf, sizeof(buf), retval, auxerror); if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_DEBUG, "bad password: %s", msg); pam_error(pamh, _("BAD PASSWORD: %s"), msg); if (getuid() || options.enforce_for_root || (flags & PAM_CHANGE_EXPIRED_AUTHTOK)) { pam_set_item(pamh, PAM_AUTHTOK, NULL); retval = PAM_AUTHTOK_ERR; continue; } } else { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_DEBUG, "password score: %d", retval); } retval = pam_get_authtok_verify(pamh, &newtoken, NULL); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "pam_get_authtok_verify returned error: %s", pam_strerror(pamh, retval)); pam_set_item(pamh, PAM_AUTHTOK, NULL); continue; } else if (newtoken == NULL) { /* user aborted password change, quit */ return PAM_AUTHTOK_ERR; } return PAM_SUCCESS; } pam_set_item (pamh, PAM_AUTHTOK, NULL); /* if we have only one try, we can use the real reason, * else say that there were too many tries. */ if (options.retry_times > 1) return PAM_MAXTRIES; else return retval; } else { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_NOTICE, "UNKNOWN flags setting %02X",flags); } return PAM_SERVICE_ERR; }
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv) { const char *username; const void *password; const char *database = NULL; const char *cryptmode = NULL; int retval = PAM_AUTH_ERR, ctrl; /* parse arguments */ ctrl = _pam_parse(pamh, argc, argv, &database, &cryptmode); if (database == NULL) { pam_syslog(pamh, LOG_ERR, "can not get the database name"); return PAM_SERVICE_ERR; } /* Get the username */ retval = pam_get_user(pamh, &username, NULL); if ((retval != PAM_SUCCESS) || (!username)) { pam_syslog(pamh, LOG_ERR, "can not get the username"); return PAM_SERVICE_ERR; } if ((ctrl & PAM_USE_FPASS_ARG) == 0 && (ctrl & PAM_TRY_FPASS_ARG) == 0) { /* Converse to obtain a password */ retval = obtain_authtok(pamh); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "can not obtain password from user"); return retval; } } /* Check if we got a password */ retval = pam_get_item(pamh, PAM_AUTHTOK, &password); if (retval != PAM_SUCCESS || password == NULL) { if ((ctrl & PAM_TRY_FPASS_ARG) != 0) { /* Converse to obtain a password */ retval = obtain_authtok(pamh); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "can not obtain password from user"); return retval; } retval = pam_get_item(pamh, PAM_AUTHTOK, &password); } if (retval != PAM_SUCCESS || password == NULL) { pam_syslog(pamh, LOG_ERR, "can not recover user password"); return PAM_AUTHTOK_RECOVERY_ERR; } } if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_INFO, "Verify user `%s' with a password", username); /* Now use the username to look up password in the database file */ retval = user_lookup(pamh, database, cryptmode, username, password, ctrl); switch (retval) { case -2: /* some sort of system error. The log was already printed */ return PAM_SERVICE_ERR; case -1: /* incorrect password */ pam_syslog(pamh, LOG_WARNING, "user `%s' denied access (incorrect password)", username); return PAM_AUTH_ERR; case 1: /* the user does not exist in the database */ if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_NOTICE, "user `%s' not found in the database", username); return PAM_USER_UNKNOWN; case 0: /* Otherwise, the authentication looked good */ pam_syslog(pamh, LOG_NOTICE, "user '%s' granted access", username); return PAM_SUCCESS; default: /* we don't know anything about this return value */ pam_syslog(pamh, LOG_ERR, "internal module error (retval = %d, user = `%s'", retval, username); return PAM_SERVICE_ERR; } /* should not be reached */ return PAM_IGNORE; }
/* authenticates user on remote TACACS+ server * returns PAM_SUCCESS if the supplied username and password * pair is valid */ PAM_EXTERN int pam_sm_authenticate (pam_handle_t * pamh, int flags, int argc, const char **argv) { int ctrl, retval; char *user; char *pass; char *tty; char *r_addr; int srv_i; int tac_fd, status, msg, communicating; user = pass = tty = r_addr = NULL; ctrl = _pam_parse(argc, argv); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)", __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); if ((user = _pam_get_user(pamh)) == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: user [%s] obtained", __FUNCTION__, user); retval = tacacs_get_password (pamh, flags, ctrl, &pass); if (retval != PAM_SUCCESS || pass == NULL || *pass == '\0') { _pam_log(LOG_ERR, "unable to obtain password"); return PAM_CRED_INSUFFICIENT; } retval = pam_set_item (pamh, PAM_AUTHTOK, pass); if (retval != PAM_SUCCESS) { _pam_log(LOG_ERR, "unable to set password"); return PAM_CRED_INSUFFICIENT; } if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: password obtained", __FUNCTION__); tty = _pam_get_terminal(pamh); if (!strncmp(tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: tty [%s] obtained", __FUNCTION__, tty); r_addr = _pam_get_rhost(pamh); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: rhost [%s] obtained", __FUNCTION__, r_addr); status = PAM_AUTHINFO_UNAVAIL; for (srv_i = 0; srv_i < tac_srv_no; srv_i++) { if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i ); tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key, NULL, tac_timeout); if (tac_fd < 0) { _pam_log(LOG_ERR, "connection failed srv %d: %m", srv_i); continue; } if (tac_authen_send(tac_fd, user, pass, tty, r_addr, TAC_PLUS_AUTHEN_LOGIN) < 0) { close(tac_fd); _pam_log(LOG_ERR, "error sending auth req to TACACS+ server"); continue; } communicating = 1; while (communicating) { struct areply re = { .attr = NULL, .msg = NULL, status = 0, flags = 0 }; struct pam_message conv_msg = { .msg_style = 0, .msg = NULL }; struct pam_response *resp = NULL; msg = tac_authen_read(tac_fd, &re); if (NULL != re.msg) { conv_msg.msg = re.msg; } /* talk the protocol */ switch (msg) { case TAC_PLUS_AUTHEN_STATUS_PASS: /* success */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_PASS"); if (NULL != conv_msg.msg) { int retval = -1; conv_msg.msg_style = PAM_TEXT_INFO; retval = converse(pamh, 1, &conv_msg, &resp); if (PAM_SUCCESS == retval) { if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG)) syslog(LOG_DEBUG, "send msg=\"%s\"", conv_msg.msg); } else { _pam_log(LOG_WARNING, "%s: error sending msg=\"%s\", retval=%d", __FUNCTION__, conv_msg.msg, retval); } } status = PAM_SUCCESS; communicating = 0; active_server.addr = tac_srv[srv_i].addr; active_server.key = tac_srv[srv_i].key; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: active srv %d", __FUNCTION__, srv_i); break; case TAC_PLUS_AUTHEN_STATUS_FAIL: if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_FAIL"); if (NULL != conv_msg.msg) { int retval = -1; conv_msg.msg_style = PAM_ERROR_MSG; retval = converse(pamh, 1, &conv_msg, &resp); if (PAM_SUCCESS == retval) { if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG)) syslog(LOG_DEBUG, "send msg=\"%s\"", conv_msg.msg); } else { _pam_log(LOG_WARNING, "%s: error sending msg=\"%s\", retval=%d", __FUNCTION__, conv_msg.msg, retval); } } status = PAM_AUTH_ERR; communicating = 0; _pam_log(LOG_ERR, "auth failed: %d", msg); break; case TAC_PLUS_AUTHEN_STATUS_GETDATA: if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG)) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETDATA"); if (NULL != conv_msg.msg) { int retval = -1; int echo_off = (0x1 == (re.flags & 0x1)); conv_msg.msg_style = echo_off ? PAM_PROMPT_ECHO_OFF : PAM_PROMPT_ECHO_ON; retval = converse(pamh, 1, &conv_msg, &resp); if (PAM_SUCCESS == retval) { if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG)) syslog(LOG_DEBUG, "sent msg=\"%s\", resp=\"%s\"", conv_msg.msg, resp->resp); if (PAM_TAC_DEBUG == (ctrl & PAM_TAC_DEBUG)) syslog(LOG_DEBUG, "%s: calling tac_cont_send", __FUNCTION__); if (0 > tac_cont_send_seq(tac_fd, resp->resp, re.seq_no + 1)) { _pam_log(LOG_ERR, "error sending continue req to TACACS+ server"); status = PAM_AUTH_ERR; communicating = 0; } } else { _pam_log(LOG_WARNING, "%s: error sending msg=\"%s\", retval=%d (%s)", __FUNCTION__, conv_msg.msg, retval, pam_strerror(pamh, retval)); status = PAM_AUTH_ERR; communicating = 0; } } else { syslog(LOG_ERR, "GETDATA response with no message, returning PAM_AUTH_ERR"); status = PAM_AUTH_ERR; communicating = 0; } break; case TAC_PLUS_AUTHEN_STATUS_GETUSER: /* not implemented */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETUSER"); communicating = 0; break; case TAC_PLUS_AUTHEN_STATUS_GETPASS: if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_GETPASS"); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__); if (tac_cont_send(tac_fd, pass) < 0) { _pam_log (LOG_ERR, "error sending continue req to TACACS+ server"); communicating = 0; } /* continue the while loop; go read tac response */ break; case TAC_PLUS_AUTHEN_STATUS_RESTART: /* try it again */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_RESTART"); /* * not implemented * WdJ: I *think* you can just do tac_authen_send(user, pass) again * but I'm not sure */ communicating = 0; break; case TAC_PLUS_AUTHEN_STATUS_ERROR: /* server has problems */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_ERROR"); communicating = 0; break; case TAC_PLUS_AUTHEN_STATUS_FOLLOW: /* server tells to try a different server address */ /* not implemented */ if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: TAC_PLUS_AUTHEN_STATUS_FOLLOW"); communicating = 0; break; default: if (msg < 0) { /* connection error */ communicating = 0; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "error communicating with tacacs server"); break; } /* unknown response code */ communicating = 0; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "tacacs status: unknown response 0x%02x", msg); } if (NULL != resp) { free(resp->resp); free(resp); } free(re.msg); } /* end while(communicating) */ close(tac_fd); if (status == PAM_SUCCESS || status == PAM_AUTH_ERR) break; } if (status != PAM_SUCCESS && status != PAM_AUTH_ERR) _pam_log(LOG_ERR, "no more servers to connect"); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: exit with pam status: %d", __FUNCTION__, status); if (NULL != pass) { bzero(pass, strlen (pass)); free(pass); pass = NULL; } return status; } /* pam_sm_authenticate */ /* no-op function to satisfy PAM authentication module */ PAM_EXTERN int pam_sm_setcred (pam_handle_t * pamh, int flags, int argc, const char **argv) { int ctrl = _pam_parse (argc, argv); if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)" , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); return PAM_SUCCESS; } /* pam_sm_setcred */ /* authorizes user on remote TACACS+ server, i.e. checks * his permission to access requested service * returns PAM_SUCCESS if the service is allowed */ PAM_EXTERN int pam_sm_acct_mgmt (pam_handle_t * pamh, int flags, int argc, const char **argv) { int retval, ctrl, status=PAM_AUTH_ERR; char *user; char *tty; char *r_addr; struct areply arep; struct tac_attrib *attr = NULL; int tac_fd; user = tty = r_addr = NULL; /* this also obtains service name for authorization this should be normally performed by pam_get_item(PAM_SERVICE) but since PAM service names are incompatible TACACS+ we have to pass it via command line argument until a better solution is found ;) */ ctrl = _pam_parse (argc, argv); if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: called (pam_tacplus v%u.%u.%u)" , __FUNCTION__, PAM_TAC_VMAJ, PAM_TAC_VMIN, PAM_TAC_VPAT); if ((user = _pam_get_user(pamh)) == NULL) return PAM_USER_UNKNOWN; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: username obtained [%s]", __FUNCTION__, user); tty = _pam_get_terminal(pamh); if(!strncmp(tty, "/dev/", 5)) tty += 5; if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: tty obtained [%s]", __FUNCTION__, tty); r_addr = _pam_get_rhost(pamh); if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: rhost obtained [%s]", __FUNCTION__, r_addr); /* checks if user has been successfully authenticated by TACACS+; we cannot solely authorize user if it hasn't been authenticated or has been authenticated by method other than TACACS+ */ if(active_server.addr == NULL) { _pam_log (LOG_ERR, "user not authenticated by TACACS+"); return PAM_AUTH_ERR; } if (ctrl & PAM_TAC_DEBUG) syslog (LOG_DEBUG, "%s: active server is [%s]", __FUNCTION__, tac_ntop(active_server.addr->ai_addr)); /* checks for specific data required by TACACS+, which should be supplied in command line */ if(!*tac_service) { _pam_log (LOG_ERR, "SM: TACACS+ service type not configured"); return PAM_AUTH_ERR; } if(tac_protocol == NULL || !*tac_protocol) { _pam_log (LOG_ERR, "SM: TACACS+ protocol type not configured (IGNORED)"); } tac_add_attrib(&attr, "service", tac_service); if(tac_protocol != NULL && tac_protocol[0] != '\0') tac_add_attrib(&attr, "protocol", tac_protocol); tac_fd = tac_connect_single(active_server.addr, active_server.key, NULL, tac_timeout); if(tac_fd < 0) { _pam_log (LOG_ERR, "TACACS+ server unavailable"); if(arep.msg != NULL) free (arep.msg); close(tac_fd); return PAM_AUTH_ERR; } retval = tac_author_send(tac_fd, user, tty, r_addr, attr); tac_free_attrib(&attr); if(retval < 0) { _pam_log (LOG_ERR, "error getting authorization"); if(arep.msg != NULL) free (arep.msg); close(tac_fd); return PAM_AUTH_ERR; } if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: sent authorization request", __FUNCTION__); tac_author_read(tac_fd, &arep); if(arep.status != AUTHOR_STATUS_PASS_ADD && arep.status != AUTHOR_STATUS_PASS_REPL) { _pam_log (LOG_ERR, "TACACS+ authorisation failed for [%s]", user); if(arep.msg != NULL) free (arep.msg); close(tac_fd); return PAM_PERM_DENIED; } if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: user [%s] successfully authorized", __FUNCTION__, user); status = PAM_SUCCESS; attr = arep.attr; while (attr != NULL) { char attribute[attr->attr_len]; char value[attr->attr_len]; char *sep; sep = index(attr->attr, '='); if(sep == NULL) sep = index(attr->attr, '*'); if(sep != NULL) { bcopy(attr->attr, attribute, attr->attr_len-strlen(sep)); attribute[attr->attr_len-strlen(sep)] = '\0'; bcopy(sep, value, strlen(sep)); value[strlen(sep)] = '\0'; size_t i; for (i = 0; attribute[i] != '\0'; i++) { attribute[i] = toupper(attribute[i]); if (attribute[i] == '-') attribute[i] = '_'; } if (ctrl & PAM_TAC_DEBUG) syslog(LOG_DEBUG, "%s: returned attribute `%s%s' from server", __FUNCTION__, attribute, value); /* make returned attributes available for other PAM modules via PAM environment */ if (pam_putenv(pamh, strncat(attribute, value, strlen(value))) != PAM_SUCCESS) syslog(LOG_WARNING, "%s: unable to set PAM environment", __FUNCTION__); } else { syslog(LOG_WARNING, "%s: invalid attribute `%s', no separator", __FUNCTION__, attr->attr); } attr = attr->next; } /* free returned attributes */ if(arep.attr != NULL) tac_free_attrib(&arep.attr); if(arep.msg != NULL) free (arep.msg); close(tac_fd); return status; } /* pam_sm_acct_mgmt */