int helper_verify_password(const char *name, const char *p, int nullok) { struct passwd *pwd = NULL; char *salt = NULL; int retval; retval = get_pwd_hash(name, &pwd, &salt); if (pwd == NULL || salt == NULL) { helper_log_err(LOG_WARNING, "check pass; user unknown"); retval = PAM_USER_UNKNOWN; } else { retval = verify_pwd_hash(p, salt, nullok); } if (salt) { _pam_overwrite(salt); _pam_drop(salt); } p = NULL; /* no longer needed here */ return retval; }
/* * Conversation function to obtain the user's password */ static int obtain_authtok(pam_handle_t *pamh) { char *resp; const void *item; int retval; retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, &resp, _("Password: ")); if (retval != PAM_SUCCESS) return retval; if (resp == NULL) return PAM_CONV_ERR; /* set the auth token */ retval = pam_set_item(pamh, PAM_AUTHTOK, resp); /* clean it up */ _pam_overwrite(resp); _pam_drop(resp); if ( (retval != PAM_SUCCESS) || (retval = pam_get_item(pamh, PAM_AUTHTOK, &item)) != PAM_SUCCESS ) { return retval; } return retval; }
static void drop_reply(struct pam_response *reply, int replies) { int i; for (i=0; i<replies; ++i) { _pam_overwrite(reply[i].resp); /* might be a password */ free(reply[i].resp); } if (reply) free(reply); }
void _whawty_cleanup(whawty_ctx_t* ctx) { _pam_overwrite(ctx->password_); _pam_drop(ctx->password_); _pam_drop(ctx->sockpath_); if(ctx->sock_ >= 0) { close(ctx->sock_); } _whawty_logf(ctx, LOG_DEBUG, "done cleaning up"); }
static int report_mail(pam_handle_t *pamh, int ctrl , const char *type, const char *folder) { int retval; if (!(ctrl & PAM_MAIL_SILENT) || ((ctrl & PAM_QUIET_MAIL) && strcmp(type, "new"))) { char *remark; if (ctrl & PAM_STANDARD_MAIL) if (!strcmp(type, "no")) remark = malloc(strlen(NO_MAIL_STANDARD_FORMAT)+1); else remark = malloc(strlen(YOUR_MAIL_STANDARD_FORMAT)+strlen(type)+1); else remark = malloc(strlen(YOUR_MAIL_VERBOSE_FORMAT)+strlen(type)+strlen(folder)+1); if (remark == NULL) { retval = PAM_BUF_ERR; } else { struct pam_message msg[1], *mesg[1]; struct pam_response *resp=NULL; if (ctrl & PAM_STANDARD_MAIL) if (!strcmp(type, "no")) sprintf(remark, NO_MAIL_STANDARD_FORMAT); else sprintf(remark, YOUR_MAIL_STANDARD_FORMAT, type); else sprintf(remark, YOUR_MAIL_VERBOSE_FORMAT, type, folder); mesg[0] = &msg[0]; msg[0].msg_style = PAM_TEXT_INFO; msg[0].msg = remark; retval = converse(pamh, ctrl, 1, mesg, &resp); _pam_overwrite(remark); _pam_drop(remark); if (resp) _pam_drop_reply(resp, 1); } } else { D(("keeping quiet")); retval = PAM_SUCCESS; } D(("returning %s", pam_strerror(pamh, retval))); return retval; }
static int say(pam_handle_t *pamh, int style, const char *format, ...) { va_list args; char buffer[0x800]; int needed; struct pam_response *resp; int status; va_start(args, format); needed = vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); if ((unsigned int)needed < sizeof(buffer)) { status = converse(pamh, style, buffer, &resp); _pam_overwrite(buffer); } else { status = PAM_ABORT; memset(buffer, 0, sizeof(buffer)); } return status; }
void _pam_drop_env(pam_handle_t *pamh) { D(("called.")); IF_NO_PAMH("_pam_make_env", pamh, /* nothing to return */); if (pamh->env != NULL) { int i; /* we will only purge the pamh->env->requested number of elements */ for (i=pamh->env->requested-1; i-- > 0; ) { D(("dropping #%3d>%s<", i, pamh->env->list[i])); _pam_overwrite(pamh->env->list[i]); /* clean */ _pam_drop(pamh->env->list[i]); /* forget */ } pamh->env->requested = 0; pamh->env->entries = 0; _pam_drop(pamh->env->list); /* forget */ _pam_drop(pamh->env); /* forget */ } else { D(("no environment present in pamh?")); } }
static int lookup(const char *name, const char *list, const char **_user) { int anon = 0; *_user = name; /* this is the default */ if (list && *list) { const char *l; char *list_copy, *x; char *sptr = NULL; list_copy = strdup(list); x = list_copy; while (list_copy && (l = strtok_r(x, ",", &sptr))) { x = NULL; if (!strcmp(name, l)) { *_user = list; anon = 1; } } _pam_overwrite(list_copy); _pam_drop(list_copy); } else { #define MAX_L 2 static const char *l[MAX_L] = { "ftp", "anonymous" }; int i; for (i=0; i<MAX_L; ++i) { if (!strcmp(l[i], name)) { *_user = l[0]; anon = 1; break; } } } return anon; }
static char *crypt_md5_wrapper(const char *pass_new) { /* * Code lifted from Marek Michalkiewicz's shadow suite. (CG) * removed use of static variables (AGM) */ struct timeval tv; MD5_CTX ctx; unsigned char result[16]; char *cp = (char *) result; unsigned char tmp[16]; int i; char *x, *e = NULL; GoodMD5Init(&ctx); gettimeofday(&tv, (struct timezone *) 0); GoodMD5Update(&ctx, (void *) &tv, sizeof tv); i = getpid(); GoodMD5Update(&ctx, (void *) &i, sizeof i); i = clock(); GoodMD5Update(&ctx, (void *) &i, sizeof i); GoodMD5Update(&ctx, result, sizeof result); GoodMD5Final(tmp, &ctx); strcpy(cp, "$1$"); /* magic for the MD5 */ cp += strlen(cp); for (i = 0; i < 8; i++) *cp++ = i64c(tmp[i] & 077); *cp = '\0'; /* no longer need cleartext */ e = Goodcrypt_md5(pass_new, (const char *) result); x = x_strdup(e); /* put e in malloc()ed memory */ _pam_overwrite(e); /* clean up */ return x; }
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; } }
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; }
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) { params_t params; struct pam_response *resp; struct passwd *pw, fake_pw; #ifdef HAVE_SHADOW struct spwd *spw; #endif char *user, *oldpass, *newpass, *randompass; const char *reason; int ask_oldauthtok; int randomonly, enforce, retries_left, retry_wanted; int status; params = defaults; status = parse(¶ms, pamh, argc, argv); if (status != PAM_SUCCESS) return status; ask_oldauthtok = 0; if (flags & PAM_PRELIM_CHECK) { if (params.flags & F_ASK_OLDAUTHTOK_PRELIM) ask_oldauthtok = 1; } else if (flags & PAM_UPDATE_AUTHTOK) { if (params.flags & F_ASK_OLDAUTHTOK_UPDATE) ask_oldauthtok = 1; } else return PAM_SERVICE_ERR; if (ask_oldauthtok && getuid() != 0) { status = converse(pamh, PAM_PROMPT_ECHO_OFF, PROMPT_OLDPASS, &resp); if (status == PAM_SUCCESS) { if (resp && resp->resp) { status = pam_set_item(pamh, PAM_OLDAUTHTOK, resp->resp); _pam_drop_reply(resp, 1); } else status = PAM_AUTHTOK_RECOVERY_ERR; } if (status != PAM_SUCCESS) return status; } if (flags & PAM_PRELIM_CHECK) return status; status = pam_get_item(pamh, PAM_USER, (pam_item_t *)&user); if (status != PAM_SUCCESS) return status; status = pam_get_item(pamh, PAM_OLDAUTHTOK, (pam_item_t *)&oldpass); if (status != PAM_SUCCESS) return status; if (params.flags & F_NON_UNIX) { pw = &fake_pw; pw->pw_name = user; pw->pw_gecos = ""; } else { pw = getpwnam(user); endpwent(); if (!pw) return PAM_USER_UNKNOWN; if ((params.flags & F_CHECK_OLDAUTHTOK) && getuid() != 0) { if (!oldpass) status = PAM_AUTH_ERR; else #ifdef HAVE_SHADOW if (!strcmp(pw->pw_passwd, "x")) { spw = getspnam(user); endspent(); if (spw) { if (strcmp(crypt(oldpass, spw->sp_pwdp), spw->sp_pwdp)) status = PAM_AUTH_ERR; memset(spw->sp_pwdp, 0, strlen(spw->sp_pwdp)); } else status = PAM_AUTH_ERR; } else #endif if (strcmp(crypt(oldpass, pw->pw_passwd), pw->pw_passwd)) status = PAM_AUTH_ERR; } memset(pw->pw_passwd, 0, strlen(pw->pw_passwd)); if (status != PAM_SUCCESS) return status; } randomonly = params.qc.min[4] > params.qc.max; if (getuid() != 0) enforce = params.flags & F_ENFORCE_USERS; else enforce = params.flags & F_ENFORCE_ROOT; if (params.flags & F_USE_AUTHTOK) { status = pam_get_item(pamh, PAM_AUTHTOK, (pam_item_t *)&newpass); if (status != PAM_SUCCESS) return status; if (!newpass || (check_max(¶ms, pamh, newpass) && enforce)) return PAM_AUTHTOK_ERR; reason = _passwdqc_check(¶ms.qc, newpass, oldpass, pw); if (reason) { say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason); if (enforce) status = PAM_AUTHTOK_ERR; } return status; } retries_left = params.retry; retry: retry_wanted = 0; if (!randomonly && params.qc.passphrase_words && params.qc.min[2] <= params.qc.max) status = say(pamh, PAM_TEXT_INFO, MESSAGE_INTRO_BOTH); else status = say(pamh, PAM_TEXT_INFO, MESSAGE_INTRO_PASSWORD); if (status != PAM_SUCCESS) return status; if (!randomonly && params.qc.min[3] <= params.qc.min[4]) status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSWORD_1, params.qc.min[3] == 8 || params.qc.min[3] == 11 ? "n" : "", params.qc.min[3]); else if (!randomonly) status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSWORD_2, params.qc.min[3] == 8 || params.qc.min[3] == 11 ? "n" : "", params.qc.min[3], params.qc.min[4] == 8 || params.qc.min[4] == 11 ? "n" : "", params.qc.min[4]); if (status != PAM_SUCCESS) return status; if (!randomonly && params.qc.passphrase_words && params.qc.min[2] <= params.qc.max) { status = say(pamh, PAM_TEXT_INFO, MESSAGE_EXPLAIN_PASSPHRASE, params.qc.passphrase_words, params.qc.min[2], params.qc.max); if (status != PAM_SUCCESS) return status; } randompass = _passwdqc_random(¶ms.qc); if (randompass) { status = say(pamh, PAM_TEXT_INFO, randomonly ? MESSAGE_RANDOMONLY : MESSAGE_RANDOM, randompass); if (status != PAM_SUCCESS) { _pam_overwrite(randompass); randompass = NULL; } } else if (randomonly) { say(pamh, PAM_ERROR_MSG, getuid() != 0 ? MESSAGE_MISCONFIGURED : MESSAGE_RANDOMFAILED); return PAM_AUTHTOK_ERR; } status = converse(pamh, PAM_PROMPT_ECHO_OFF, PROMPT_NEWPASS1, &resp); if (status == PAM_SUCCESS && (!resp || !resp->resp)) status = PAM_AUTHTOK_ERR; if (status != PAM_SUCCESS) { if (randompass) _pam_overwrite(randompass); return status; } newpass = strdup(resp->resp); _pam_drop_reply(resp, 1); if (!newpass) { if (randompass) _pam_overwrite(randompass); return PAM_AUTHTOK_ERR; } if (check_max(¶ms, pamh, newpass) && enforce) { status = PAM_AUTHTOK_ERR; retry_wanted = 1; } reason = NULL; if (status == PAM_SUCCESS && (!randompass || !strstr(newpass, randompass)) && (randomonly || (reason = _passwdqc_check(¶ms.qc, newpass, oldpass, pw)))) { if (randomonly) say(pamh, PAM_ERROR_MSG, MESSAGE_NOTRANDOM); else say(pamh, PAM_ERROR_MSG, MESSAGE_WEAKPASS, reason); if (enforce) { status = PAM_AUTHTOK_ERR; retry_wanted = 1; } } if (status == PAM_SUCCESS) status = converse(pamh, PAM_PROMPT_ECHO_OFF, PROMPT_NEWPASS2, &resp); if (status == PAM_SUCCESS) { if (resp && resp->resp) { if (strcmp(newpass, resp->resp)) { status = say(pamh, PAM_ERROR_MSG, MESSAGE_MISTYPED); if (status == PAM_SUCCESS) { status = PAM_AUTHTOK_ERR; retry_wanted = 1; } } _pam_drop_reply(resp, 1); } else status = PAM_AUTHTOK_ERR; } if (status == PAM_SUCCESS) status = pam_set_item(pamh, PAM_AUTHTOK, newpass); if (randompass) _pam_overwrite(randompass); _pam_overwrite(newpass); free(newpass); if (retry_wanted && --retries_left > 0) { status = say(pamh, PAM_TEXT_INFO, MESSAGE_RETRY); if (status == PAM_SUCCESS) goto retry; } return status; }
static int get_folder(pam_handle_t *pamh, int ctrl, char **path_mail, char **folder_p, int hashcount) { int retval; const char *user, *path; char *folder; const struct passwd *pwd=NULL; retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS || user == NULL) { _log_err(LOG_ERR, "no user specified"); return PAM_USER_UNKNOWN; } if (ctrl & PAM_NEW_MAIL_DIR) { path = *path_mail; if (*path == '~') { /* support for $HOME delivery */ pwd = getpwnam(user); if (pwd == NULL) { _log_err(LOG_ERR, "user [%s] unknown", user); _pam_overwrite(*path_mail); _pam_drop(*path_mail); return PAM_USER_UNKNOWN; } /* * "~/xxx" and "~xxx" are treated as same */ if (!*++path || (*path == '/' && !*++path)) { _log_err(LOG_ALERT, "badly formed mail path [%s]", *path_mail); _pam_overwrite(*path_mail); _pam_drop(*path_mail); return PAM_ABORT; } ctrl |= PAM_HOME_MAIL; if (hashcount != 0) { _log_err(LOG_ALERT, "can't do hash= and home directory mail"); } } } else { path = DEFAULT_MAIL_DIRECTORY; } /* put folder together */ if (ctrl & PAM_HOME_MAIL) { folder = malloc(sizeof(MAIL_FILE_FORMAT) +strlen(pwd->pw_dir)+strlen(path)); } else { folder = malloc(sizeof(MAIL_FILE_FORMAT)+strlen(path)+strlen(user) +2*hashcount); } if (folder != NULL) { if (ctrl & PAM_HOME_MAIL) { sprintf(folder, MAIL_FILE_FORMAT, pwd->pw_dir, "", path); } else { int i; char *hash = malloc(2*hashcount+1); if (hash) { for (i = 0; i < hashcount; i++) { hash[2*i] = '/'; hash[2*i+1] = user[i]; } hash[2*i] = '\0'; sprintf(folder, MAIL_FILE_FORMAT, path, hash, user); _pam_overwrite(hash); _pam_drop(hash); } else { sprintf(folder, "error"); } } D(("folder =[%s]", folder)); } /* tidy up */ _pam_overwrite(*path_mail); _pam_drop(*path_mail); user = NULL; if (folder == NULL) { _log_err(LOG_CRIT, "out of memory for mail folder"); return PAM_BUF_ERR; } *folder_p = folder; folder = NULL; return PAM_SUCCESS; }
int misc_conv(int num_msg, const struct pam_message **msgm, struct pam_response **response, void *appdata_ptr) { int count=0; struct pam_response *reply; if (num_msg <= 0) return PAM_CONV_ERR; D(("allocating empty response structure array.")); reply = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response)); if (reply == NULL) { D(("no memory for responses")); return PAM_CONV_ERR; } D(("entering conversation function.")); for (count=0; count < num_msg; ++count) { char *string=NULL; switch (msgm[count]->msg_style) { case PAM_PROMPT_ECHO_OFF: string = read_string(CONV_ECHO_OFF,msgm[count]->msg); if (string == NULL) { goto failed_conversation; } break; case PAM_PROMPT_ECHO_ON: string = read_string(CONV_ECHO_ON,msgm[count]->msg); if (string == NULL) { goto failed_conversation; } break; case PAM_ERROR_MSG: if (fprintf(stderr,"%s\n",msgm[count]->msg) < 0) { goto failed_conversation; } break; case PAM_TEXT_INFO: if (fprintf(stdout,"%s\n",msgm[count]->msg) < 0) { goto failed_conversation; } break; case PAM_BINARY_PROMPT: { pamc_bp_t binary_prompt = NULL; if (!msgm[count]->msg || !pam_binary_handler_fn) { goto failed_conversation; } PAM_BP_RENEW(&binary_prompt, PAM_BP_RCONTROL(msgm[count]->msg), PAM_BP_LENGTH(msgm[count]->msg)); PAM_BP_FILL(binary_prompt, 0, PAM_BP_LENGTH(msgm[count]->msg), PAM_BP_RDATA(msgm[count]->msg)); if (pam_binary_handler_fn(appdata_ptr, &binary_prompt) != PAM_SUCCESS || (binary_prompt == NULL)) { goto failed_conversation; } string = (char *) binary_prompt; binary_prompt = NULL; break; } default: fprintf(stderr, "erroneous conversation (%d)\n" ,msgm[count]->msg_style); goto failed_conversation; } if (string) { /* must add to reply array */ /* add string to list of responses */ reply[count].resp_retcode = 0; reply[count].resp = string; string = NULL; } } /* New (0.59+) behavior is to always have a reply - this is compatable with the X/Open (March 1997) spec. */ *response = reply; reply = NULL; return PAM_SUCCESS; failed_conversation: if (reply) { for (count=0; count<num_msg; ++count) { if (reply[count].resp == NULL) { continue; } switch (msgm[count]->msg_style) { case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: _pam_overwrite(reply[count].resp); free(reply[count].resp); break; case PAM_BINARY_PROMPT: pam_binary_handler_free(appdata_ptr, (pamc_bp_t *) &reply[count].resp); break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: /* should not actually be able to get here... */ free(reply[count].resp); } reply[count].resp = NULL; } /* forget reply too */ free(reply); reply = NULL; } return PAM_CONV_ERR; }
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; }
int pam_end(pam_handle_t *pamh, int pam_status) { int ret; D(("entering pam_end()")); IF_NO_PAMH("pam_end", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from module!?")); return PAM_SYSTEM_ERR; } /* first liberate the modules (it is not inconcevible that the modules may need to use the service_name etc. to clean up) */ _pam_free_data(pamh, pam_status); /* now drop all modules */ if ((ret = _pam_free_handlers(pamh)) != PAM_SUCCESS) { return ret; /* error occurred */ } /* from this point we cannot call the modules any more. Free the remaining memory used by the Linux-PAM interface */ _pam_drop_env(pamh); /* purge the environment */ _pam_overwrite(pamh->authtok); /* blank out old token */ _pam_drop(pamh->authtok); _pam_overwrite(pamh->oldauthtok); /* blank out old token */ _pam_drop(pamh->oldauthtok); _pam_overwrite(pamh->former.prompt); _pam_drop(pamh->former.prompt); /* drop saved prompt */ _pam_overwrite(pamh->service_name); _pam_drop(pamh->service_name); _pam_overwrite(pamh->user); _pam_drop(pamh->user); _pam_overwrite(pamh->prompt); _pam_drop(pamh->prompt); /* prompt for pam_get_user() */ _pam_overwrite(pamh->tty); _pam_drop(pamh->tty); _pam_overwrite(pamh->rhost); _pam_drop(pamh->rhost); _pam_overwrite(pamh->ruser); _pam_drop(pamh->ruser); _pam_drop(pamh->pam_conversation); pamh->fail_delay.delay_fn_ptr = NULL; /* and finally liberate the memory for the pam_handle structure */ _pam_drop(pamh); D(("exiting pam_end() successfully")); return PAM_SUCCESS; }
int verify_pwd_hash(const char *p, char *hash, unsigned int nullok) { size_t hash_len; char *pp = NULL; int retval; D(("called")); strip_hpux_aging(hash); hash_len = strlen(hash); if (!hash_len) { /* the stored password is NULL */ if (nullok) { /* this means we've succeeded */ D(("user has empty password - access granted")); retval = PAM_SUCCESS; } else { D(("user has empty password - access denied")); retval = PAM_AUTH_ERR; } } else if (!p || *hash == '*' || *hash == '!') { retval = PAM_AUTH_ERR; } else { if (!strncmp(hash, "$1$", 3)) { pp = Goodcrypt_md5(p, hash); if (pp && strcmp(pp, hash) != 0) { _pam_delete(pp); pp = Brokencrypt_md5(p, hash); } } else if (*hash != '$' && hash_len >= 13) { pp = bigcrypt(p, hash); if (pp && hash_len == 13 && strlen(pp) > hash_len) { _pam_overwrite(pp + hash_len); } } else { /* * Ok, we don't know the crypt algorithm, but maybe * libcrypt knows about it? We should try it. */ #ifdef HAVE_CRYPT_R struct crypt_data *cdata; cdata = malloc(sizeof(*cdata)); if (cdata != NULL) { cdata->initialized = 0; pp = x_strdup(crypt_r(p, hash, cdata)); memset(cdata, '\0', sizeof(*cdata)); free(cdata); } #else pp = x_strdup(crypt(p, hash)); #endif } p = NULL; /* no longer needed here */ /* the moment of truth -- do we agree with the password? */ D(("comparing state of pp[%s] and hash[%s]", pp, hash)); if (pp && strcmp(pp, hash) == 0) { retval = PAM_SUCCESS; } else { retval = PAM_AUTH_ERR; } } if (pp) _pam_delete(pp); D(("done [%d].", retval)); return retval; }
static int last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t *lltime) { struct flock last_lock; struct lastlog last_login; int retval = PAM_SUCCESS; char the_time[256]; char *date = NULL; char *host = NULL; char *line = NULL; memset(&last_lock, 0, sizeof(last_lock)); last_lock.l_type = F_RDLCK; last_lock.l_whence = SEEK_SET; last_lock.l_start = sizeof(last_login) * (off_t) uid; last_lock.l_len = sizeof(last_login); if (fcntl(last_fd, F_SETLK, &last_lock) < 0) { D(("locking %s failed..(waiting a little)", _PATH_LASTLOG)); pam_syslog(pamh, LOG_WARNING, "file %s is locked/read", _PATH_LASTLOG); sleep(LASTLOG_IGNORE_LOCK_TIME); } if (pam_modutil_read(last_fd, (char *) &last_login, sizeof(last_login)) != sizeof(last_login)) { memset(&last_login, 0, sizeof(last_login)); } last_lock.l_type = F_UNLCK; (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */ *lltime = last_login.ll_time; if (!last_login.ll_time) { if (announce & LASTLOG_DEBUG) { pam_syslog(pamh, LOG_DEBUG, "first login for user with uid %lu", (unsigned long int)uid); } } if (!(announce & LASTLOG_QUIET)) { if (last_login.ll_time) { /* we want the date? */ if (announce & LASTLOG_DATE) { struct tm *tm, tm_buf; time_t ll_time; ll_time = last_login.ll_time; tm = localtime_r (&ll_time, &tm_buf); strftime (the_time, sizeof (the_time), /* TRANSLATORS: "strftime options for date of last login" */ _(" %a %b %e %H:%M:%S %Z %Y"), tm); date = the_time; } /* we want & have the host? */ if ((announce & LASTLOG_HOST) && (last_login.ll_host[0] != '\0')) { /* TRANSLATORS: " from <host>" */ if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE, last_login.ll_host) < 0) { pam_syslog(pamh, LOG_ERR, "out of memory"); retval = PAM_BUF_ERR; goto cleanup; } } /* we want and have the terminal? */ if ((announce & LASTLOG_LINE) && (last_login.ll_line[0] != '\0')) { /* TRANSLATORS: " on <terminal>" */ if (asprintf(&line, _(" on %.*s"), UT_LINESIZE, last_login.ll_line) < 0) { pam_syslog(pamh, LOG_ERR, "out of memory"); retval = PAM_BUF_ERR; goto cleanup; } } if (date != NULL || host != NULL || line != NULL) /* TRANSLATORS: "Last login: <date> from <host> on <terminal>" */ retval = pam_info(pamh, _("Last login:%s%s%s"), date ? date : "", host ? host : "", line ? line : ""); } else if (announce & LASTLOG_NEVER) { D(("this is the first time this user has logged in")); retval = pam_info(pamh, "%s", _("Welcome to your new account!")); } } /* cleanup */ cleanup: memset(&last_login, 0, sizeof(last_login)); _pam_overwrite(date); _pam_overwrite(host); _pam_drop(host); _pam_overwrite(line); _pam_drop(line); return retval; }
int pam_end(pam_handle_t *pamh, int pam_status) { int ret; D(("entering pam_end()")); IF_NO_PAMH("pam_end", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from module!?")); return PAM_SYSTEM_ERR; } #ifdef HAVE_LIBAUDIT _pam_audit_end(pamh, pam_status); #endif #ifdef CONFIG_PROP_STATSD_STATSD if (pam_status == PAM_SUCCESS) { char buf[MAX_PAM_STATS_BUF_SIZE]; memset(buf,'\0',MAX_PAM_STATS_BUF_SIZE); snprintf(buf, MAX_PAM_STATS_BUF_SIZE-1, "statsd incr pam_succeeded_%s %s", pamh->user,pamh->service_name); if (system(buf) == -1) { pam_syslog(pamh, LOG_INFO, "%s %s statsd incr failed", buf, pamh->service_name); } snprintf(buf, MAX_PAM_STATS_BUF_SIZE-1, "statsd incr pam_users %s", pamh->user); if (system(buf) == -1) { pam_syslog(pamh, LOG_INFO, "%s - failed", buf); } snprintf(buf, MAX_PAM_STATS_BUF_SIZE-1, "statsd incr pam_services %s", pamh->service_name); if (system(buf) == -1) { pam_syslog(pamh, LOG_INFO, "%s - failed", buf); } } #endif /* first liberate the modules (it is not inconcevible that the modules may need to use the service_name etc. to clean up) */ _pam_free_data(pamh, pam_status); /* now drop all modules */ if ((ret = _pam_free_handlers(pamh)) != PAM_SUCCESS) { return ret; /* error occurred */ } /* from this point we cannot call the modules any more. Free the remaining memory used by the Linux-PAM interface */ _pam_drop_env(pamh); /* purge the environment */ _pam_overwrite(pamh->authtok); /* blank out old token */ _pam_drop(pamh->authtok); _pam_overwrite(pamh->oldauthtok); /* blank out old token */ _pam_drop(pamh->oldauthtok); _pam_overwrite(pamh->former.prompt); _pam_drop(pamh->former.prompt); /* drop saved prompt */ _pam_overwrite(pamh->service_name); _pam_drop(pamh->service_name); _pam_overwrite(pamh->user); _pam_drop(pamh->user); _pam_overwrite(pamh->prompt); _pam_drop(pamh->prompt); /* prompt for pam_get_user() */ _pam_overwrite(pamh->tty); _pam_drop(pamh->tty); _pam_overwrite(pamh->rhost); _pam_drop(pamh->rhost); _pam_overwrite(pamh->ruser); _pam_drop(pamh->ruser); _pam_drop(pamh->pam_conversation); pamh->fail_delay.delay_fn_ptr = NULL; /* and finally liberate the memory for the pam_handle structure */ _pam_drop(pamh); D(("exiting pam_end() successfully")); return PAM_SUCCESS; }
int pam_get_user(pam_handle_t *pamh, const char **user, const char *prompt) { const char *use_prompt; int retval; struct pam_message msg,*pmsg; struct pam_response *resp; D(("called.")); IF_NO_PAMH("pam_get_user", pamh, PAM_SYSTEM_ERR); if (pamh->pam_conversation == NULL) { _pam_system_log(LOG_ERR, "pam_get_user: no conv element in pamh"); return PAM_SERVICE_ERR; } if (user == NULL) { /* ensure the the module has suplied a destination */ _pam_system_log(LOG_ERR, "pam_get_user: nowhere to record username"); return PAM_PERM_DENIED; } else *user = NULL; if (pamh->user) { /* have one so return it */ *user = pamh->user; return PAM_SUCCESS; } /* will need a prompt */ use_prompt = prompt; if (use_prompt == NULL) { use_prompt = pamh->prompt; if (use_prompt == NULL) { use_prompt = PAM_DEFAULT_PROMPT; } } /* If we are resuming an old conversation, we verify that the prompt is the same. Anything else is an error. */ if (pamh->former.want_user) { /* must have a prompt to resume with */ if (! pamh->former.prompt) { _pam_system_log(LOG_ERR, "pam_get_user: failed to resume with prompt" ); return PAM_ABORT; } /* must be the same prompt as last time */ if (strcmp(pamh->former.prompt, use_prompt)) { _pam_system_log(LOG_ERR, "pam_get_user: resumed with different prompt"); return PAM_ABORT; } /* ok, we can resume where we left off last time */ pamh->former.want_user = PAM_FALSE; _pam_overwrite(pamh->former.prompt); _pam_drop(pamh->former.prompt); } /* converse with application -- prompt user for a username */ pmsg = &msg; msg.msg_style = PAM_PROMPT_ECHO_ON; msg.msg = use_prompt; resp = NULL; retval = pamh->pam_conversation-> conv(1, (const struct pam_message **) &pmsg, &resp, pamh->pam_conversation->appdata_ptr); if (retval == PAM_CONV_AGAIN) { /* conversation function is waiting for an event - save state */ D(("conversation function is not ready yet")); pamh->former.want_user = PAM_TRUE; pamh->former.prompt = _pam_strdup(use_prompt); } else if (resp == NULL) { /* * conversation should have given a response */ D(("pam_get_user: no response provided")); retval = PAM_CONV_ERR; } else if (retval == PAM_SUCCESS) { /* copy the username */ /* * now we set the PAM_USER item -- this was missing from pre.53 * releases. However, reading the Sun manual, it is part of * the standard API. */ RESET(pamh->user, resp->resp); *user = pamh->user; } if (resp) { /* * note 'resp' is allocated by the application and is * correctly free()'d here */ _pam_drop_reply(resp, 1); } D(("completed")); return retval; /* pass on any error from conversation */ }
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; } }
int pam_putenv(pam_handle_t *pamh, const char *name_value) { int l2eq, item, retval; D(("called.")); IF_NO_PAMH("pam_putenv", pamh, PAM_ABORT); if (name_value == NULL) { pam_syslog(pamh, LOG_ERR, "pam_putenv: no variable indicated"); return PAM_PERM_DENIED; } /* * establish if we are setting or deleting; scan for '=' */ for (l2eq=0; name_value[l2eq] && name_value[l2eq] != '='; ++l2eq); if (l2eq <= 0) { pam_syslog(pamh, LOG_ERR, "pam_putenv: bad variable"); return PAM_BAD_ITEM; } /* * Look first for environment. */ if (pamh->env == NULL || pamh->env->list == NULL) { pam_syslog(pamh, LOG_ERR, "pam_putenv: no env%s found", pamh->env == NULL ? "":"-list"); return PAM_ABORT; } /* find the item to replace */ item = _pam_search_env(pamh->env, name_value, l2eq); if (name_value[l2eq]) { /* (re)setting */ if (item == -1) { /* new variable */ D(("adding item: %s", name_value)); /* enough space? */ if (pamh->env->entries <= pamh->env->requested) { register int i; register char **tmp; /* get some new space */ tmp = calloc( pamh->env->entries + PAM_ENV_CHUNK , sizeof(char *) ); if (tmp == NULL) { /* nothing has changed - old env intact */ pam_syslog(pamh, LOG_CRIT, "pam_putenv: cannot grow environment"); return PAM_BUF_ERR; } /* copy old env-item pointers/forget old */ for (i=0; i<pamh->env->requested; ++i) { tmp[i] = pamh->env->list[i]; pamh->env->list[i] = NULL; } /* drop old list and replace with new */ _pam_drop(pamh->env->list); pamh->env->list = tmp; pamh->env->entries += PAM_ENV_CHUNK; D(("resized env list")); _pam_dump_env(pamh); /* only when debugging */ } item = pamh->env->requested-1; /* old last item (NULL) */ /* add a new NULL entry at end; increase counter */ pamh->env->list[pamh->env->requested++] = NULL; } else { /* replace old */ D(("replacing item: %s\n with: %s" , pamh->env->list[item], name_value)); _pam_overwrite(pamh->env->list[item]); _pam_drop(pamh->env->list[item]); } /* * now we have a place to put the new env-item, insert at 'item' */ pamh->env->list[item] = _pam_strdup(name_value); if (pamh->env->list[item] != NULL) { _pam_dump_env(pamh); /* only when debugging */ return PAM_SUCCESS; } /* something went wrong; we should delete the item - fall through */ retval = PAM_BUF_ERR; /* an error occurred */ } else { retval = PAM_SUCCESS; /* we requested delete */ } /* getting to here implies we are deleting an item */ if (item < 0) { pam_syslog(pamh, LOG_ERR, "pam_putenv: delete non-existent entry; %s", name_value); return PAM_BAD_ITEM; } /* * remove item: purge memory; reset counter; resize [; display-env] */ D(("deleting: env#%3d:[%s]", item, pamh->env->list[item])); _pam_overwrite(pamh->env->list[item]); _pam_drop(pamh->env->list[item]); --(pamh->env->requested); D(("mmove: item[%d]+%d -> item[%d]" , item+1, ( pamh->env->requested - item ), item)); (void) memmove(&pamh->env->list[item], &pamh->env->list[item+1] , ( pamh->env->requested - item )*sizeof(char *) ); _pam_dump_env(pamh); /* only when debugging */ /* * deleted. */ return retval; }
static int knet_pam_misc_conv(int num_msg, const struct pam_message **msgm, struct pam_response **response, void *appdata_ptr) { int count = 0; struct pam_response *reply; struct knet_vty *vty = (struct knet_vty *)appdata_ptr; if (num_msg <= 0) return PAM_CONV_ERR; reply = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response)); if (reply == NULL) return PAM_CONV_ERR; for (count=0; count < num_msg; ++count) { unsigned char readbuf[VTY_MAX_BUFFER_SIZE]; char *string=NULL; int nc; memset(readbuf, 0, sizeof(readbuf)); switch (msgm[count]->msg_style) { case PAM_PROMPT_ECHO_OFF: if (knet_vty_set_echo(vty, 0) < 0) { knet_vty_write(vty, "Unable to turn off terminal/telnet echo"); goto failed_conversation; } knet_vty_write(vty, "%s", msgm[count]->msg); nc = knet_vty_read(vty, readbuf, sizeof(readbuf)); if (nc < 0) goto failed_conversation; if (knet_vty_set_echo(vty, 1) < 0) { /* doesn't really make a lot of sense tho.... */ knet_vty_write(vty, "Unable to turn on terminal/telnet echo"); goto failed_conversation; } knet_vty_write(vty, "\n"); readbuf[nc-2] = 0; string = strdup((const char*)readbuf); if (!string) goto failed_conversation; break; case PAM_PROMPT_ECHO_ON: knet_vty_write(vty, "\n%s", msgm[count]->msg); nc = knet_vty_read(vty, readbuf, sizeof(readbuf)); if (nc < 0) goto failed_conversation; readbuf[nc-2] = 0; string = strdup((const char*)readbuf); if (!string) goto failed_conversation; break; case PAM_ERROR_MSG: log_error("Received PAM error message %s", msgm[count]->msg); knet_vty_write(vty, "%s", msgm[count]->msg); break; case PAM_TEXT_INFO: log_error("Received PAM text info: %s", msgm[count]->msg); knet_vty_write(vty, "%s", msgm[count]->msg); break; default: if (!vty->got_epipe) { log_error("Unknown PAM conversation message"); knet_vty_write(vty, "Unknown PAM conversation message"); } goto failed_conversation; } if (string) { reply[count].resp_retcode = 0; reply[count].resp = string; string = NULL; } } *response = reply; reply = NULL; return PAM_SUCCESS; failed_conversation: if (!vty->got_epipe) { log_error("PAM conversation error"); knet_vty_write(vty, "PAM conversation error"); } if (reply) { for (count=0; count < num_msg; ++count) { if (reply[count].resp == NULL) continue; switch (msgm[count]->msg_style) { case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: _pam_overwrite(reply[count].resp); free(reply[count].resp); break; case PAM_BINARY_PROMPT: { void *bt_ptr = reply[count].resp; pam_binary_handler_free(appdata_ptr, bt_ptr); break; } case PAM_ERROR_MSG: case PAM_TEXT_INFO: free(reply[count].resp); } } free(reply); reply = NULL; } return PAM_CONV_ERR; }
/* read a line of input string, giving prompt when appropriate */ static char *read_string(int echo, const char *prompt) { struct termios term_before, term_tmp; char line[INPUTSIZE]; struct sigaction old_sig; int delay, nc, have_term=0; D(("called with echo='%s', prompt='%s'.", echo ? "ON":"OFF" , prompt)); if (isatty(STDIN_FILENO)) { /* terminal state */ /* is a terminal so record settings and flush it */ if ( tcgetattr(STDIN_FILENO, &term_before) != 0 ) { D(("<error: failed to get terminal settings>")); return NULL; } memcpy(&term_tmp, &term_before, sizeof(term_tmp)); if (!echo) { term_tmp.c_lflag &= ~(ECHO); } have_term = 1; } else if (!echo) { D(("<warning: cannot turn echo off>")); } /* set up the signal handling */ delay = get_delay(); /* reading the line */ while (delay >= 0) { fprintf(stderr, "%s", prompt); /* this may, or may not set echo off -- drop pending input */ if (have_term) (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_tmp); if ( delay > 0 && set_alarm(delay, &old_sig) ) { D(("<failed to set alarm>")); break; } else { nc = read(STDIN_FILENO, line, INPUTSIZE-1); if (have_term) { (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before); if (!echo || expired) /* do we need a newline? */ fprintf(stderr,"\n"); } if ( delay > 0 ) { reset_alarm(&old_sig); } if (expired) { delay = get_delay(); } else if (nc > 0) { /* we got some user input */ char *input; if (nc > 0 && line[nc-1] == '\n') { /* <NUL> terminate */ line[--nc] = '\0'; } else { line[nc] = '\0'; } input = x_strdup(line); _pam_overwrite(line); return input; /* return malloc()ed string */ } else if (nc == 0) { /* Ctrl-D */ D(("user did not want to type anything")); fprintf(stderr, "\n"); break; } } } /* getting here implies that the timer expired */ if (have_term) (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before); memset(line, 0, INPUTSIZE); /* clean up */ return NULL; }
/* * Change a user's password in NetInfo. */ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, const char **argv) { char *oldHash, *newHash; char *oldPassword = NULL, *newPassword = NULL; void *d; int status, isroot, tries, maxTries; int options = 0; int amChangingExpiredPassword; ni_id dir; ni_proplist pl; ni_property p; ni_namelist nl; int ni_uid, uid, secure, minlen, lifetime; ni_index where; struct pam_conv *appconv; struct pam_message msg, *pmsg; struct pam_response *resp; const char *cmiscptr = NULL; char *uname; char salt[9]; int i; amChangingExpiredPassword = flags & PAM_CHANGE_EXPIRED_AUTHTOK; status = pam_get_item(pamh, PAM_CONV, (void **) &appconv); if (status != PAM_SUCCESS) return status; status = pam_get_item(pamh, PAM_USER, (void **) &uname); if (status != PAM_SUCCESS) return status; if (uname == NULL) return PAM_USER_UNKNOWN; status = pam_get_item(pamh, PAM_OLDAUTHTOK, (void **) &oldPassword); if (status != PAM_SUCCESS) { return status; } if (pam_test_option(&options, PAM_OPT_USE_FIRST_PASS, NULL) || pam_test_option(&options, PAM_OPT_TRY_FIRST_PASS, NULL)) { if (pam_get_item(pamh, PAM_AUTHTOK, (void **) &newPassword) != PAM_SUCCESS) newPassword = NULL; if (pam_test_option(&options, PAM_OPT_USE_FIRST_PASS, NULL) && newPassword == NULL) return PAM_AUTHTOK_RECOVER_ERR; } d = domain_for_user(uname, NULL, &dir); if (d == (void *) NULL) { syslog(LOG_ERR, "user %s not found in NetInfo", uname); return PAM_USER_UNKNOWN; } /* * These should be configurable in NetInfo. */ secure = secure_passwords(); maxTries = secure ? 3 : 5; minlen = secure ? 8 : 5; /* * Read the passwd and uid from NetInfo. */ status = ni_lookupprop(d, &dir, "passwd", &nl); if (status == NI_NOPROP) nl.ni_namelist_len = 0; else if (status != NI_OK) { ni_free(d); syslog(LOG_ERR, "NetInfo read failed: %s", ni_error(status)); return netinfo2PamStatus(status); } oldHash = NULL; if (nl.ni_namelist_len > 0) oldHash = nl.ni_namelist_val[0]; status = ni_lookupprop(d, &dir, "uid", &nl); if (status != NI_OK) { ni_free(d); syslog(LOG_ERR, "NetInfo read failed: %s", ni_error(status)); return netinfo2PamStatus(status); } ni_uid = -2; if (nl.ni_namelist_len > 0) ni_uid = atoi(nl.ni_namelist_val[0]); /* * See if I'm uid 0 on the master host for the user's NetInfo domain. */ isroot = is_root_on_master(d); uid = getuid(); if (isroot) { if (flags & PAM_PRELIM_CHECK) { /* Don't need old password. */ return PAM_SUCCESS; } } else if (uid != ni_uid) { ni_free(d); return PAM_PERM_DENIED; } if (flags & PAM_PRELIM_CHECK) { /* * If we are not root, we should verify the old * password. */ char *encrypted; if (oldPassword != NULL && (pam_test_option(&options, PAM_OPT_USE_FIRST_PASS, NULL) || pam_test_option(&options, PAM_OPT_TRY_FIRST_PASS, NULL))) { encrypted = crypt(oldPassword, oldHash); if (oldPassword[0] == '\0' && oldHash != '\0') encrypted = ":"; status = strcmp(encrypted, oldHash) == 0 ? PAM_SUCCESS : PAM_AUTH_ERR; if (status != PAM_SUCCESS) { if (pam_test_option(&options, PAM_OPT_USE_FIRST_PASS, NULL)) sendConversationMessage(appconv, "NetInfo password incorrect", PAM_ERROR_MSG, &options); else sendConversationMessage(appconv, "NetInfo password incorrect: try again", PAM_ERROR_MSG, &options); } else { ni_free(d); return PAM_SUCCESS; } } tries = 0; while (oldPassword == NULL && tries++ < maxTries) { pmsg = &msg; msg.msg_style = PAM_PROMPT_ECHO_OFF; msg.msg = OLD_PASSWORD_PROMPT; resp = NULL; status = appconv->conv(1, (struct pam_message **) & pmsg, &resp, appconv->appdata_ptr); if (status != PAM_SUCCESS) { ni_free(d); return status; } oldPassword = resp->resp; free(resp); encrypted = crypt(oldPassword, oldHash); if (oldPassword[0] == '\0' && oldHash != '\0') encrypted = ":"; status = strcmp(encrypted, oldHash) == 0 ? PAM_SUCCESS : PAM_AUTH_ERR; if (status != PAM_SUCCESS) { int abortMe = 0; if (oldPassword != NULL && oldPassword[0] == '\0') abortMe = 1; _pam_overwrite(oldPassword); _pam_drop(oldPassword); if (!amChangingExpiredPassword & abortMe) { sendConversationMessage(appconv, "Password change aborted", PAM_ERROR_MSG, &options); ni_free(d); return PAM_AUTHTOK_RECOVER_ERR; } else { sendConversationMessage(appconv, "NetInfo password incorrect: try again", PAM_ERROR_MSG, &options); } } } if (oldPassword == NULL) { status = PAM_MAXTRIES; } (void) pam_set_item(pamh, PAM_OLDAUTHTOK, oldPassword); ni_free(d); return status; } /* PAM_PRELIM_CHECK */ status = PAM_ABORT; tries = 0; while (newPassword == NULL && tries++ < maxTries) { pmsg = &msg; msg.msg_style = PAM_PROMPT_ECHO_OFF; msg.msg = NEW_PASSWORD_PROMPT; resp = NULL; status = appconv->conv(1, &pmsg, &resp, appconv->appdata_ptr); if (status != PAM_SUCCESS) { ni_free(d); return status; } newPassword = resp->resp; free(resp); if (newPassword[0] == '\0') { free(newPassword); newPassword = NULL; } if (newPassword != NULL) { if (isroot == 0) { if (oldPassword != NULL && !strcmp(oldPassword, newPassword)) { cmiscptr = "Passwords must differ"; newPassword = NULL; } else if (strlen(newPassword) < minlen) { cmiscptr = "Password too short"; newPassword = NULL; } } } else { ni_free(d); return PAM_AUTHTOK_RECOVER_ERR; } if (cmiscptr == NULL) { /* get password again */ char *miscptr; pmsg = &msg; msg.msg_style = PAM_PROMPT_ECHO_OFF; msg.msg = AGAIN_PASSWORD_PROMPT; resp = NULL; status = appconv->conv(1, &pmsg, &resp, appconv->appdata_ptr); if (status != PAM_SUCCESS) { ni_free(d); return status; } miscptr = resp->resp; free(resp); if (miscptr[0] == '\0') { free(miscptr); miscptr = NULL; } if (miscptr == NULL) { if (!amChangingExpiredPassword) { sendConversationMessage(appconv, "Password change aborted", PAM_ERROR_MSG, &options); ni_free(d); return PAM_AUTHTOK_RECOVER_ERR; } } else if (!strcmp(newPassword, miscptr)) { miscptr = NULL; break; } sendConversationMessage(appconv, "You must enter the same password", PAM_ERROR_MSG, &options); miscptr = NULL; newPassword = NULL; } else { sendConversationMessage(appconv, cmiscptr, PAM_ERROR_MSG, &options); cmiscptr = NULL; newPassword = NULL; } } if (cmiscptr != NULL || newPassword == NULL) { ni_free(d); return PAM_MAXTRIES; } /* * Lock onto the master server. */ ni_needwrite(d, 1); /* * Authenticate if necessary */ if (isroot == 0) { ni_setuser(d, uname); ni_setpassword(d, oldPassword); } /* * Create a random salt */ srandom((int) time((time_t *) NULL)); salt[0] = saltchars[random() % strlen(saltchars)]; salt[1] = saltchars[random() % strlen(saltchars)]; salt[2] = '\0'; newHash = crypt(newPassword, salt); /* * Change the password in NetInfo. */ status = ni_read(d, &dir, &pl); if (status != NI_OK) { ni_free(d); syslog(LOG_ERR, "NetInfo read failed: %s", ni_error(status)); return netinfo2PamStatus(status); } p.nip_name = "passwd"; p.nip_val.ni_namelist_len = 1; p.nip_val.ni_namelist_val = (ni_name *) malloc(sizeof(ni_name)); p.nip_val.ni_namelist_val[0] = newHash; where = ni_proplist_match(pl, p.nip_name, NULL); if (where == NI_INDEX_NULL) status = ni_createprop(d, &dir, p, NI_INDEX_NULL); else status = ni_writeprop(d, &dir, where, p.nip_val); if (status != NI_OK) { ni_free(d); syslog(LOG_ERR, "NetInfo write property \"passwd\" failed: %s", ni_error(status)); return netinfo2PamStatus(status); } /* * Now, update "change" property. If this fails, we've still * updated the password... perhaps the user should be alerted * of this. */ lifetime = password_lifetime(); if (lifetime > 0) { struct timeval tp; char change[64]; where = ni_proplist_match(pl, "change", NULL); gettimeofday(&tp, NULL); tp.tv_sec += lifetime; snprintf(change, sizeof(change), "%ld", tp.tv_sec); p.nip_name = "change"; p.nip_val.ni_namelist_len = 1; p.nip_val.ni_namelist_val[0] = change; if (where == NI_INDEX_NULL) status = ni_createprop(d, &dir, p, NI_INDEX_NULL); else status = ni_writeprop(d, &dir, where, p.nip_val); if (status != NI_OK) { ni_free(d); syslog(LOG_ERR, "NetInfo write property \"change\" failed: %s", ni_error(status)); return netinfo2PamStatus(status); } } free(p.nip_val.ni_namelist_val); ni_free(d); /* tell lookupd to invalidate its cache */ { int i, proc = -1; unit lookup_buf[MAX_INLINE_UNITS]; #ifdef __NeXT__ port_t port; #else mach_port_t port; #endif port = _lookupd_port(0); (void) _lookup_link(port, "_invalidatecache", &proc); (void) _lookup_one(port, proc, NULL, 0, lookup_buf, &i); } return PAM_SUCCESS; }
int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { const char *values[] = { "\x70\x72", // "pr" => pierre "\x70\x70", // "pp" => papier "\x63\x73"}; // "cs" => ciseaux char prompt_text[32] = ""; const char *want = ""; char *response = NULL; int debug = 0; int ret, fd, r, i; unsigned char c; for (i = 0; i < argc; i++) { if (strcmp(argv[i], "debug") == 0) { debug = 1; break; } } r = -1; for (i = 0; i < argc; i++) { if (strncmp(argv[i], "throw=", 6) == 0) { r = atol(argv[i] + 6) % 3; break; } } if (r == -1) { r = 0; fd = open("/dev/urandom", O_RDONLY); if (fd != -1) { c = 0; do { ret = read(fd, &c, 1); } while ( ((ret == 1) && (c == 0xff)) || ((ret == -1) && (errno == EINTR)) ); /* We drop 0xff here to avoid a variation on * Bleichenbacher's attack. */ r = c / 85; close(fd); } else { /* Something is wrong with /dev/urandom */ return PAM_CONV_ERR; } } strcpy(prompt_text, values[(r % 3)]); want = values[((r + 1) % 3)]; if (debug) { pam_syslog(pamh, LOG_DEBUG, "challenge is \"%s\", " "expected response is \"%s\"", prompt_text, want); } ret = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, &response, "%s: ", prompt_text); if (ret != PAM_SUCCESS) { pam_syslog(pamh, LOG_CRIT, "conversation error"); return PAM_CONV_ERR; } if ((response != NULL) && (strcasecmp(response, want) == 0)) { ret = PAM_SUCCESS; } else { ret = PAM_AUTH_ERR; } if (response) { _pam_overwrite(response); free(response); } return ret; }
int pam_set_item (pam_handle_t *pamh, int item_type, const void *item) { int retval; D(("called")); IF_NO_PAMH("pam_set_item", pamh, PAM_SYSTEM_ERR); retval = PAM_SUCCESS; switch (item_type) { case PAM_SERVICE: /* Setting handlers_loaded to 0 will cause the handlers * to be reloaded on the next call to a service module. */ pamh->handlers.handlers_loaded = 0; RESET(pamh->service_name, item); { char *tmp; for (tmp=pamh->service_name; *tmp; ++tmp) *tmp = tolower(*tmp); /* require lower case */ } break; case PAM_USER: RESET(pamh->user, item); break; case PAM_USER_PROMPT: RESET(pamh->prompt, item); break; case PAM_TTY: D(("setting tty to %s", item)); RESET(pamh->tty, item); break; case PAM_RUSER: RESET(pamh->ruser, item); break; case PAM_RHOST: RESET(pamh->rhost, item); break; case PAM_AUTHTOK: /* * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from * modules. */ if (__PAM_FROM_MODULE(pamh)) { char *_TMP_ = pamh->authtok; if (_TMP_ == item) /* not changed so leave alone */ break; pamh->authtok = (item) ? _pam_strdup(item) : NULL; if (_TMP_) { _pam_overwrite(_TMP_); free(_TMP_); } } else { retval = PAM_BAD_ITEM; } break; case PAM_OLDAUTHTOK: /* * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from * modules. */ if (__PAM_FROM_MODULE(pamh)) { char *_TMP_ = pamh->oldauthtok; if (_TMP_ == item) /* not changed so leave alone */ break; pamh->oldauthtok = (item) ? _pam_strdup(item) : NULL; if (_TMP_) { _pam_overwrite(_TMP_); free(_TMP_); } } else { retval = PAM_BAD_ITEM; } break; case PAM_CONV: /* want to change the conversation function */ if (item == NULL) { _pam_system_log(LOG_ERR, "pam_set_item: attempt to set conv() to NULL"); retval = PAM_PERM_DENIED; } else { struct pam_conv *tconv; if ((tconv= (struct pam_conv *) malloc(sizeof(struct pam_conv)) ) == NULL) { _pam_system_log(LOG_CRIT, "pam_set_item: malloc failed for pam_conv"); retval = PAM_BUF_ERR; } else { memcpy(tconv, item, sizeof(struct pam_conv)); _pam_drop(pamh->pam_conversation); pamh->pam_conversation = tconv; } } break; case PAM_FAIL_DELAY: pamh->fail_delay.delay_fn_ptr = item; break; default: retval = PAM_BAD_ITEM; } return retval; }
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, const char **argv) { unsigned int ctrl, lctrl; int retval, i; int remember = -1; /* <DO NOT free() THESE> */ const char *user; char *pass_old, *pass_new; /* </DO NOT free() THESE> */ D(("called.")); #ifdef USE_LCKPWDF /* our current locking system requires that we lock the entire password database. This avoids both livelock and deadlock. */ /* These values for the number of attempts and the sleep time are, of course, completely arbitrary. My reading of the PAM docs is that, once pam_chauthtok() has been called with PAM_UPDATE_AUTHTOK, we are obliged to take any reasonable steps to make sure the token is updated; so retrying for 1/10 sec. isn't overdoing it. The other possibility is to call lckpwdf() on the first pam_chauthtok() pass, and hold the lock until released in the second pass--but is this guaranteed to work? -SRL */ i=0; while((retval = lckpwdf()) != 0 && i < 100) { usleep(1000); } if(retval != 0) { return PAM_AUTHTOK_LOCK_BUSY; } #endif ctrl = _set_ctrl(pamh, flags, &remember, argc, argv); /* * First get the name of a user */ retval = pam_get_user(pamh, &user, "Username: "******"bad username [%s]", user); #ifdef USE_LCKPWDF ulckpwdf(); #endif return PAM_USER_UNKNOWN; } if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) _log_err(LOG_DEBUG, pamh, "username [%s] obtained", user); } else { if (on(UNIX_DEBUG, ctrl)) _log_err(LOG_DEBUG, pamh, "password - could not identify user"); #ifdef USE_LCKPWDF ulckpwdf(); #endif return retval; } D(("Got username of %s", user)); /* * This is not an AUTH module! */ if (on(UNIX__NONULL, ctrl)) set(UNIX__NULLOK, ctrl); if (on(UNIX__PRELIM, ctrl)) { /* * obtain and verify the current password (OLDAUTHTOK) for * the user. */ char *Announce; D(("prelim check")); if (_unix_blankpasswd(ctrl, user)) { #ifdef USE_LCKPWDF ulckpwdf(); #endif return PAM_SUCCESS; } else if (off(UNIX__IAMROOT, ctrl)) { /* instruct user what is happening */ #define greeting "Changing password for " Announce = (char *) malloc(sizeof(greeting) + strlen(user)); if (Announce == NULL) { _log_err(LOG_CRIT, pamh, "password - out of memory"); #ifdef USE_LCKPWDF ulckpwdf(); #endif return PAM_BUF_ERR; } (void) strcpy(Announce, greeting); (void) strcpy(Announce + sizeof(greeting) - 1, user); #undef greeting lctrl = ctrl; set(UNIX__OLD_PASSWD, lctrl); retval = _unix_read_password(pamh, lctrl ,Announce ,"(current) UNIX password: "******"password - (old) token not obtained"); #ifdef USE_LCKPWDF ulckpwdf(); #endif return retval; } /* verify that this is the password for this user */ retval = _unix_verify_password(pamh, user, pass_old, ctrl); } else { D(("process run by root so do nothing this time around")); pass_old = NULL; retval = PAM_SUCCESS; /* root doesn't have too */ } if (retval != PAM_SUCCESS) { D(("Authentication failed")); pass_old = NULL; #ifdef USE_LCKPWDF ulckpwdf(); #endif return retval; } retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old); pass_old = NULL; if (retval != PAM_SUCCESS) { _log_err(LOG_CRIT, pamh, "failed to set PAM_OLDAUTHTOK"); } retval = _unix_verify_shadow(user, ctrl); if (retval == PAM_AUTHTOK_ERR) { if (off(UNIX__IAMROOT, ctrl)) _make_remark(pamh, ctrl, PAM_ERROR_MSG, "You must wait longer to change your password"); else retval = PAM_SUCCESS; } } else if (on(UNIX__UPDATE, ctrl)) { /* * tpass is used below to store the _pam_md() return; it * should be _pam_delete()'d. */ char *tpass = NULL; int retry = 0; /* * obtain the proposed password */ D(("do update")); /* * get the old token back. NULL was ok only if root [at this * point we assume that this has already been enforced on a * previous call to this function]. */ if (off(UNIX_NOT_SET_PASS, ctrl)) { retval = pam_get_item(pamh, PAM_OLDAUTHTOK ,(const void **) &pass_old); } else { retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK ,(const void **) &pass_old); if (retval == PAM_NO_MODULE_DATA) { retval = PAM_SUCCESS; pass_old = NULL; } } D(("pass_old [%s]", pass_old)); if (retval != PAM_SUCCESS) { _log_err(LOG_NOTICE, pamh, "user not authenticated"); #ifdef USE_LCKPWDF ulckpwdf(); #endif return retval; } retval = _unix_verify_shadow(user, ctrl); if (retval != PAM_SUCCESS) { _log_err(LOG_NOTICE, pamh, "user not authenticated 2"); #ifdef USE_LCKPWDF ulckpwdf(); #endif return retval; } D(("get new password now")); lctrl = ctrl; if (on(UNIX_USE_AUTHTOK, lctrl)) { set(UNIX_USE_FIRST_PASS, lctrl); } 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 = _unix_read_password(pamh, lctrl ,NULL ,"Enter new UNIX password: "******"Retype new UNIX password: "******"password - new password not obtained"); } pass_old = NULL; /* tidy up */ #ifdef USE_LCKPWDF ulckpwdf(); #endif return retval; } D(("returned to _unix_chauthtok")); /* * 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; } retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new); } if (retval != PAM_SUCCESS) { _log_err(LOG_NOTICE, pamh, "new password not acceptable"); _pam_overwrite(pass_new); _pam_overwrite(pass_old); pass_new = pass_old = NULL; /* tidy up */ #ifdef USE_LCKPWDF ulckpwdf(); #endif return retval; } /* * By reaching here we have approved the passwords and must now * rebuild the password database file. */ /* * First we encrypt the new password. */ if (on(UNIX_MD5_PASS, ctrl)) { tpass = crypt_md5_wrapper(pass_new); } else { /* * Salt manipulation is stolen from Rick Faith's passwd * program. Sorry Rick :) -- alex */ time_t tm; char salt[3]; time(&tm); salt[0] = bin_to_ascii(tm & 0x3f); salt[1] = bin_to_ascii((tm >> 6) & 0x3f); salt[2] = '\0'; if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) { /* * to avoid using the _extensions_ of the bigcrypt() * function we truncate the newly entered password */ char *temp = malloc(9); char *e; if (temp == NULL) { _log_err(LOG_CRIT, pamh, "out of memory for password"); _pam_overwrite(pass_new); _pam_overwrite(pass_old); pass_new = pass_old = NULL; /* tidy up */ #ifdef USE_LCKPWDF ulckpwdf(); #endif return PAM_BUF_ERR; } /* copy first 8 bytes of password */ strncpy(temp, pass_new, 8); temp[8] = '\0'; /* no longer need cleartext */ e = bigcrypt(temp, salt); tpass = x_strdup(e); _pam_overwrite(e); _pam_delete(temp); /* tidy up */ } else { char *e; /* no longer need cleartext */ e = bigcrypt(pass_new, salt); tpass = x_strdup(e); _pam_overwrite(e); } } D(("password processed")); /* update the password database(s) -- race conditions..? */ retval = _do_setpass(pamh, user, pass_old, tpass, ctrl, remember); _pam_overwrite(pass_new); _pam_overwrite(pass_old); _pam_delete(tpass); pass_old = pass_new = NULL; } else { /* something has broken with the module */
/* use this to free strings. ESPECIALLY password strings */ static char *_pam_delete(register char *xx) { _pam_overwrite(xx); free(xx); return NULL; }
static int get_mail_status(pam_handle_t *pamh, int ctrl, const char *folder) { int type = 0; struct stat mail_st; if (stat(folder, &mail_st) < 0) return 0; if (S_ISDIR(mail_st.st_mode)) { /* Assume Maildir format */ int i, save_errno; char *dir; struct dirent **namelist; if (asprintf(&dir, "%s/new", folder) < 0) { pam_syslog(pamh, LOG_CRIT, "out of memory"); goto get_mail_status_cleanup; } i = scandir(dir, &namelist, 0, alphasort); save_errno = errno; _pam_overwrite(dir); _pam_drop(dir); if (i < 0) { type = 0; namelist = NULL; if (save_errno == ENOMEM) { pam_syslog(pamh, LOG_CRIT, "out of memory"); goto get_mail_status_cleanup; } } type = (i > 2) ? HAVE_NEW_MAIL : 0; while (--i >= 0) _pam_drop(namelist[i]); _pam_drop(namelist); if (type == 0) { if (asprintf(&dir, "%s/cur", folder) < 0) { pam_syslog(pamh, LOG_CRIT, "out of memory"); goto get_mail_status_cleanup; } i = scandir(dir, &namelist, 0, alphasort); save_errno = errno; _pam_overwrite(dir); _pam_drop(dir); if (i < 0) { type = 0; namelist = NULL; if (save_errno == ENOMEM) { pam_syslog(pamh, LOG_CRIT, "out of memory"); goto get_mail_status_cleanup; } } if (i > 2) type = HAVE_OLD_MAIL; else type = (ctrl & PAM_EMPTY_TOO) ? HAVE_NO_MAIL : 0; while (--i >= 0) _pam_drop(namelist[i]); _pam_drop(namelist); } } else { if (mail_st.st_size > 0) { if (mail_st.st_atime < mail_st.st_mtime) /* new */ type = HAVE_NEW_MAIL; else /* old */ type = (ctrl & PAM_STANDARD_MAIL) ? HAVE_MAIL : HAVE_OLD_MAIL; } else if (ctrl & PAM_EMPTY_TOO) { type = HAVE_NO_MAIL; } else { type = 0; } } get_mail_status_cleanup: memset(&mail_st, 0, sizeof(mail_st)); D(("user has %d mail in %s folder", type, folder)); return type; }