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"); }
/* * 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; }
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; }
void _pam_free_data(pam_handle_t *pamh, int status) { struct pam_data *last; struct pam_data *data; D(("called")); IF_NO_PAMH("_pam_free_data", pamh, /* no return value for void fn */); data = pamh->data; while (data) { last = data; data = data->next; if (last->cleanup) { last->cleanup(pamh, last->data, status); } _pam_drop(last->name); _pam_drop(last); } }
int pam_set_data( pam_handle_t *pamh, const char *module_data_name, void *data, void (*cleanup)(pam_handle_t *pamh, void *data, int error_status)) { struct pam_data *data_entry; D(("called")); IF_NO_PAMH("pam_set_data", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_APP(pamh)) { D(("called from application!?")); return PAM_SYSTEM_ERR; } /* module_data_name should not be NULL */ if (module_data_name == NULL) { D(("called with NULL as module_data_name")); return PAM_SYSTEM_ERR; } /* first check if there is some data already. If so clean it up */ if ((data_entry = _pam_locate_data(pamh, module_data_name))) { if (data_entry->cleanup) { data_entry->cleanup(pamh, data_entry->data, PAM_DATA_REPLACE | PAM_SUCCESS ); } } else if ((data_entry = malloc(sizeof(*data_entry)))) { char *tname; if ((tname = _pam_strdup(module_data_name)) == NULL) { pam_syslog(pamh, LOG_CRIT, "pam_set_data: no memory for data name"); _pam_drop(data_entry); return PAM_BUF_ERR; } data_entry->next = pamh->data; pamh->data = data_entry; data_entry->name = tname; } else { pam_syslog(pamh, LOG_CRIT, "pam_set_data: cannot allocate data entry"); return PAM_BUF_ERR; } data_entry->data = data; /* note this could be NULL */ data_entry->cleanup = cleanup; return PAM_SUCCESS; }
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 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; }
int _pam_make_env(pam_handle_t *pamh) { D(("called.")); IF_NO_PAMH("_pam_make_env", pamh, PAM_ABORT); /* * get structure memory */ pamh->env = (struct pam_environ *) malloc(sizeof(struct pam_environ)); if (pamh->env == NULL) { pam_syslog(pamh, LOG_CRIT, "_pam_make_env: out of memory"); return PAM_BUF_ERR; } /* * get list memory */ pamh->env->list = (char **)calloc( PAM_ENV_CHUNK, sizeof(char *) ); if (pamh->env->list == NULL) { pam_syslog(pamh, LOG_CRIT, "_pam_make_env: no memory for list"); _pam_drop(pamh->env); return PAM_BUF_ERR; } /* * fill entries in pamh->env */ pamh->env->entries = PAM_ENV_CHUNK; pamh->env->requested = 1; pamh->env->list[0] = NULL; _pam_dump_env(pamh); /* only active when debugging */ return PAM_SUCCESS; }
static int parse_credentials_buffer(const char *creds, size_t len, char **username, char **password) { char *domain; int user_len, pass_len; if (len < sizeof(int)) { return -1; } user_len = ntohl(*((int *)creds)); *username = strndup(creds + sizeof(int), user_len); if (*username == NULL) { return -1; } pass_len = len - sizeof(int) - user_len; *password = strndup(creds + sizeof(int) + user_len, pass_len); if (*password == NULL) { _pam_drop(*username); return -1; } domain = strchr(*username, '@'); if (domain != NULL) { *domain = '\0'; /* local/nis users doesn't have a domain. */ if (getpwnam(*username) == NULL) { *domain = '@'; } else { domain += 1; } } return 0; }
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 int read_field(int fd, char **buf, int *from, int *to) { /* is buf set ? */ if (! *buf) { *buf = (char *) malloc(PAM_TIME_BUFLEN); if (! *buf) { _log_err("out of memory"); D(("no memory")); return -1; } *from = *to = 0; fd = open(PAM_TIME_CONF, O_RDONLY); } /* do we have a file open ? return error */ if (fd < 0 && *to <= 0) { _log_err( PAM_TIME_CONF " not opened"); memset(*buf, 0, PAM_TIME_BUFLEN); _pam_drop(*buf); return -1; } /* check if there was a newline last time */ if ((*to > *from) && (*to > 0) && ((*buf)[*from] == '\0')) { /* previous line ended */ (*from)++; (*buf)[0] = '\0'; return fd; } /* ready for more data: first shift the buffer's remaining data */ *to -= *from; shift_bytes(*buf, *from, *to); *from = 0; (*buf)[*to] = '\0'; while (fd >= 0 && *to < PAM_TIME_BUFLEN) { int i; /* now try to fill the remainder of the buffer */ i = read(fd, *to + *buf, PAM_TIME_BUFLEN - *to); if (i < 0) { _log_err("error reading " PAM_TIME_CONF); return -1; } else if (!i) { close(fd); fd = -1; /* end of file reached */ } else *to += i; /* * contract the buffer. Delete any comments, and replace all * multiple spaces with single commas */ i = 0; #ifdef DEBUG_DUMP D(("buffer=<%s>",*buf)); #endif while (i < *to) { if ((*buf)[i] == ',') { int j; for (j=++i; j<*to && (*buf)[j] == ','; ++j); if (j!=i) { shift_bytes(i + (*buf), j-i, (*to) - j); *to -= j-i; } } switch ((*buf)[i]) { int j,c; case '#': for (j=i; j < *to && (c = (*buf)[j]) != '\n'; ++j); if (j >= *to) { (*buf)[*to = ++i] = '\0'; } else if (c == '\n') { shift_bytes(i + (*buf), j-i, (*to) - j); *to -= j-i; ++i; } else { _log_err("internal error in " __FILE__ " at line %d", __LINE__ ); return -1; } break; case '\\': if ((*buf)[i+1] == '\n') { shift_bytes(i + *buf, 2, *to - (i+2)); *to -= 2; } else { ++i; /* we don't escape non-newline characters */ } break; case '!': case ' ': case '\t': if ((*buf)[i] != '!') (*buf)[i] = ','; /* delete any trailing spaces */ for (j=++i; j < *to && ( (c = (*buf)[j]) == ' ' || c == '\t' ); ++j); shift_bytes(i + *buf, j-i, (*to)-j ); *to -= j-i; break; default: ++i; } } } (*buf)[*to] = '\0'; /* now return the next field (set the from/to markers) */ { int i; for (i=0; i<*to; ++i) { switch ((*buf)[i]) { case '#': case '\n': /* end of the line/file */ (*buf)[i] = '\0'; *from = i; return fd; case FIELD_SEPARATOR: /* end of the field */ (*buf)[i] = '\0'; *from = ++i; return fd; } } *from = i; (*buf)[*from] = '\0'; } if (*to <= 0) { D(("[end of text]")); *buf = NULL; } return fd; }
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 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 last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t lltime) { int retval; int fd; struct utmp ut; struct utmp utuser; int failed = 0; char the_time[256]; char *date = NULL; char *host = NULL; char *line = NULL; if (strlen(user) > UT_NAMESIZE) { pam_syslog(pamh, LOG_WARNING, "username too long, output might be inaccurate"); } /* obtain the failed login attempt records from btmp */ fd = open(_PATH_BTMP, O_RDONLY); if (fd < 0) { int save_errno = errno; pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_BTMP); D(("unable to open %s file", _PATH_BTMP)); if (save_errno == ENOENT) return PAM_SUCCESS; else return PAM_SERVICE_ERR; } while ((retval=pam_modutil_read(fd, (void *)&ut, sizeof(ut))) == sizeof(ut)) { if (ut.ut_tv.tv_sec >= lltime && strncmp(ut.ut_user, user, UT_NAMESIZE) == 0) { memcpy(&utuser, &ut, sizeof(utuser)); failed++; } } if (failed) { /* we want the date? */ if (announce & LASTLOG_DATE) { struct tm *tm, tm_buf; time_t lf_time; lf_time = utuser.ut_tv.tv_sec; tm = localtime_r (&lf_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) && (utuser.ut_host[0] != '\0')) { /* TRANSLATORS: " from <host>" */ if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE, utuser.ut_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) && (utuser.ut_line[0] != '\0')) { /* TRANSLATORS: " on <terminal>" */ if (asprintf(&line, _(" on %.*s"), UT_LINESIZE, utuser.ut_line) < 0) { pam_syslog(pamh, LOG_ERR, "out of memory"); retval = PAM_BUF_ERR; goto cleanup; } } if (line != NULL || date != NULL || host != NULL) { /* TRANSLATORS: "Last failed login: <date> from <host> on <terminal>" */ pam_info(pamh, _("Last failed login:%s%s%s"), date ? date : "", host ? host : "", line ? line : ""); } _pam_drop(line); #if defined HAVE_DNGETTEXT && defined ENABLE_NLS retval = asprintf (&line, dngettext(PACKAGE, "There was %d failed login attempt since the last successful login.", "There were %d failed login attempts since the last successful login.", failed), failed); #else if (failed == 1) retval = asprintf(&line, _("There was %d failed login attempt since the last successful login."), failed); else retval = asprintf(&line, /* TRANSLATORS: only used if dngettext is not supported */ _("There were %d failed login attempts since the last successful login."), failed); #endif if (retval >= 0) retval = pam_info(pamh, "%s", line); else { retval = PAM_BUF_ERR; line = NULL; } } cleanup: free(host); free(line); close(fd); D(("all done with btmp")); 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 */ }
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 security_context_t config_context (pam_handle_t *pamh, security_context_t defaultcon, int use_current_range, int debug) { security_context_t newcon=NULL; context_t new_context; int mls_enabled = is_selinux_mls_enabled(); char *response=NULL; char *type=NULL; char resp_val = 0; pam_prompt (pamh, PAM_TEXT_INFO, NULL, _("Default Security Context %s\n"), defaultcon); while (1) { if (query_response(pamh, _("Would you like to enter a different role or level?"), "n", &response, debug) == PAM_SUCCESS) { resp_val = response[0]; _pam_drop(response); } else { resp_val = 'N'; } if ((resp_val == 'y') || (resp_val == 'Y')) { if ((new_context = context_new(defaultcon)) == NULL) goto fail_set; /* Allow the user to enter role and level individually */ if (query_response(pamh, _("role:"), context_role_get(new_context), &response, debug) == PAM_SUCCESS && response[0]) { if (get_default_type(response, &type)) { pam_prompt (pamh, PAM_ERROR_MSG, NULL, _("No default type for role %s\n"), response); _pam_drop(response); continue; } else { if (context_role_set(new_context, response)) goto fail_set; if (context_type_set (new_context, type)) goto fail_set; } } _pam_drop(response); if (mls_enabled) { if (use_current_range) { security_context_t mycon = NULL; context_t my_context; if (getcon(&mycon) != 0) goto fail_set; my_context = context_new(mycon); if (my_context == NULL) { freecon(mycon); goto fail_set; } freecon(mycon); if (context_range_set(new_context, context_range_get(my_context))) { context_free(my_context); goto fail_set; } context_free(my_context); } else if (query_response(pamh, _("level:"), context_range_get(new_context), &response, debug) == PAM_SUCCESS && response[0]) { if (context_range_set(new_context, response)) goto fail_set; } _pam_drop(response); } if (debug) pam_syslog(pamh, LOG_NOTICE, "Selected Security Context %s", context_str(new_context)); /* Get the string value of the context and see if it is valid. */ if (!security_check_context(context_str(new_context))) { newcon = strdup(context_str(new_context)); if (newcon == NULL) goto fail_set; context_free(new_context); /* we have to check that this user is allowed to go into the range they have specified ... role is tied to an seuser, so that'll be checked at setexeccon time */ if (mls_enabled && !mls_range_allowed(pamh, defaultcon, newcon, debug)) { pam_syslog(pamh, LOG_NOTICE, "Security context %s is not allowed for %s", defaultcon, newcon); send_audit_message(pamh, 0, defaultcon, newcon); free(newcon); goto fail_range; } return newcon; } else { send_audit_message(pamh, 0, defaultcon, context_str(new_context)); send_text(pamh,_("Not a valid security context"),debug); } context_free(new_context); /* next time around allocates another */ } else return strdup(defaultcon); } /* end while */ return NULL; fail_set: free(type); _pam_drop(response); context_free (new_context); send_audit_message(pamh, 0, defaultcon, NULL); fail_range: return NULL; }
static security_context_t manual_context (pam_handle_t *pamh, const char *user, int debug) { security_context_t newcon=NULL; context_t new_context; int mls_enabled = is_selinux_mls_enabled(); char *type=NULL; char *response=NULL; while (1) { if (query_response(pamh, _("Would you like to enter a security context? [N] "), NULL, &response, debug) != PAM_SUCCESS) return NULL; if ((response[0] == 'y') || (response[0] == 'Y')) { if (mls_enabled) new_context = context_new ("user:role:type:level"); else new_context = context_new ("user:role:type"); if (!new_context) goto fail_set; if (context_user_set (new_context, user)) goto fail_set; _pam_drop(response); /* Allow the user to enter each field of the context individually */ if (query_response(pamh, _("role:"), NULL, &response, debug) == PAM_SUCCESS && response[0] != '\0') { if (context_role_set (new_context, response)) goto fail_set; if (get_default_type(response, &type)) goto fail_set; if (context_type_set (new_context, type)) goto fail_set; } _pam_drop(response); if (mls_enabled) { if (query_response(pamh, _("level:"), NULL, &response, debug) == PAM_SUCCESS && response[0] != '\0') { if (context_range_set (new_context, response)) goto fail_set; } _pam_drop(response); } /* Get the string value of the context and see if it is valid. */ if (!security_check_context(context_str(new_context))) { newcon = strdup(context_str(new_context)); context_free (new_context); return newcon; } else send_text(pamh,_("Not a valid security context"),debug); context_free (new_context); } else { _pam_drop(response); return NULL; } } /* end while */ fail_set: free(type); _pam_drop(response); context_free (new_context); return NULL; }
static int check_account(const char *service, const char *tty , const char *user) { int from=0,to=0,fd=-1; char *buffer=NULL; int count=0; TIME here_and_now; int retval=PAM_SUCCESS; gid_t *grps; int no_grps; /* * first we get the current list of groups - the application * will have previously done an initgroups(), or equivalent. */ D(("counting supplementary groups")); no_grps = getgroups(0, NULL); /* find the current number of groups */ if (no_grps > 0) { grps = calloc( blk_size(no_grps) , sizeof(gid_t) ); D(("copying current list into grps [%d big]",blk_size(no_grps))); (void) getgroups(no_grps, grps); #ifdef DEBUG { int z; for (z=0; z<no_grps; ++z) { D(("gid[%d]=%d", z, grps[z])); } } #endif } else { D(("no supplementary groups known")); no_grps = 0; grps = NULL; } here_and_now = time_now(); /* find current time */ /* parse the rules in the configuration file */ do { int good=TRUE; /* here we get the service name field */ fd = read_field(fd,&buffer,&from,&to); if (!buffer || !buffer[0]) { /* empty line .. ? */ continue; } ++count; D(("working on rule #%d",count)); good = logic_field(service, buffer, count, is_same); D(("with service: %s", good ? "passes":"fails" )); /* here we get the terminal name field */ fd = read_field(fd,&buffer,&from,&to); if (!buffer || !buffer[0]) { _log_err(PAM_GROUP_CONF "; no tty entry #%d", count); continue; } good &= logic_field(tty, buffer, count, is_same); D(("with tty: %s", good ? "passes":"fails" )); /* here we get the username field */ fd = read_field(fd,&buffer,&from,&to); if (!buffer || !buffer[0]) { _log_err(PAM_GROUP_CONF "; no user entry #%d", count); continue; } good &= logic_field(user, buffer, count, is_same); D(("with user: %s", good ? "passes":"fails" )); /* here we get the time field */ fd = read_field(fd,&buffer,&from,&to); if (!buffer || !buffer[0]) { _log_err(PAM_GROUP_CONF "; no time entry #%d", count); continue; } good &= logic_field(&here_and_now, buffer, count, check_time); D(("with time: %s", good ? "passes":"fails" )); fd = read_field(fd,&buffer,&from,&to); if (!buffer || !buffer[0]) { _log_err(PAM_GROUP_CONF "; no listed groups for rule #%d" , count); continue; } /* * so we have a list of groups, we need to turn it into * something to send to setgroups(2) */ if (good) { D(("adding %s to gid list", buffer)); good = mkgrplist(buffer, &grps, no_grps); if (good < 0) { no_grps = 0; } else { no_grps = good; } } /* check the line is terminated correctly */ fd = read_field(fd,&buffer,&from,&to); if (buffer && buffer[0]) { _log_err(PAM_GROUP_CONF "; poorly terminated rule #%d", count); } if (good > 0) { D(("rule #%d passed, added %d groups", count, good)); } else if (good < 0) { retval = PAM_BUF_ERR; } else { D(("rule #%d failed", count)); } } while (buffer); /* now set the groups for the user */ if (no_grps > 0) { int err; D(("trying to set %d groups", no_grps)); #ifdef DEBUG for (err=0; err<no_grps; ++err) { D(("gid[%d]=%d", err, grps[err])); } #endif if ((err = setgroups(no_grps, grps))) { D(("but couldn't set groups %d", err)); _log_err("unable to set the group membership for user (err=%d)" , err); retval = PAM_CRED_ERR; } } if (grps) { /* tidy up */ memset(grps, 0, sizeof(gid_t) * blk_size(no_grps)); _pam_drop(grps); no_grps = 0; } return retval; }
/** * Overwrite possibly sensitive memory before free-ing it. * * @param xx String containing possibly sensitive information * @retval Always NULL (useful for reseting pointer in the same line) */ static char * _pam_delete( register char * xx ) { _pam_overwrite( xx ); _pam_drop( 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; }
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; }
static int read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state) { char *to; char *src; int i; char c; int onspace; /* is buf set ? */ if (! *buf) { *buf = (char *) calloc(1, PAM_TIME_BUFLEN+1); if (! *buf) { pam_syslog(pamh, LOG_ERR, "out of memory"); D(("no memory")); *state = STATE_EOF; return -1; } *from = 0; *state = STATE_NL; fd = open(PAM_TIME_CONF, O_RDONLY); if (fd < 0) { pam_syslog(pamh, LOG_ERR, "error opening %s: %m", PAM_TIME_CONF); _pam_drop(*buf); *state = STATE_EOF; return -1; } } if (*from > 0) to = shift_buf(*buf, *from); else to = *buf; while (fd != -1 && to - *buf < PAM_TIME_BUFLEN) { i = pam_modutil_read(fd, to, PAM_TIME_BUFLEN - (to - *buf)); if (i < 0) { pam_syslog(pamh, LOG_ERR, "error reading %s: %m", PAM_TIME_CONF); close(fd); memset(*buf, 0, PAM_TIME_BUFLEN); _pam_drop(*buf); *state = STATE_EOF; return -1; } else if (!i) { close(fd); fd = -1; /* end of file reached */ } to += i; } if (to == *buf) { /* nothing previously in buf, nothing read */ _pam_drop(*buf); *state = STATE_EOF; return -1; } memset(to, '\0', PAM_TIME_BUFLEN - (to - *buf)); to = *buf; onspace = 1; /* delete any leading spaces */ for (src = to; (c=*src) != '\0'; ++src) { if (*state == STATE_COMMENT && c != '\n') { continue; } switch (c) { case '\n': *state = STATE_NL; *to = '\0'; *from = (src - *buf) + 1; trim_spaces(*buf, to); return fd; case '\t': case ' ': if (!onspace) { onspace = 1; *to++ = ' '; } break; case '!': onspace = 1; /* ignore following spaces */ *to++ = '!'; break; case '#': *state = STATE_COMMENT; break; case FIELD_SEPARATOR: *state = STATE_FIELD; *to = '\0'; *from = (src - *buf) + 1; trim_spaces(*buf, to); return fd; case '\\': if (src[1] == '\n') { ++src; /* skip it */ break; } default: *to++ = c; onspace = 0; } if (src > to) *src = '\0'; /* clearing */ } if (*state != STATE_COMMENT) { *state = STATE_COMMENT; pam_syslog(pamh, LOG_ERR, "field too long - ignored"); **buf = '\0'; } else { *to = '\0'; trim_spaces(*buf, to); } *from = 0; return fd; }
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_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; } }
/* * 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; }
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_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; }
static int get_folder(pam_handle_t *pamh, int ctrl, const char *path_mail, char **folder_p, size_t hashcount) { int retval; const char *user, *path; char *folder = NULL; const struct passwd *pwd = NULL; retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS || user == NULL) { pam_syslog(pamh, LOG_ERR, "cannot determine username"); retval = PAM_USER_UNKNOWN; goto get_folder_cleanup; } if (ctrl & PAM_NEW_MAIL_DIR) { path = path_mail; if (*path == '~') { /* support for $HOME delivery */ pwd = pam_modutil_getpwnam(pamh, user); if (pwd == NULL) { pam_syslog(pamh, LOG_ERR, "user unknown"); retval = PAM_USER_UNKNOWN; goto get_folder_cleanup; } /* * "~/xxx" and "~xxx" are treated as same */ if (!*++path || (*path == '/' && !*++path)) { pam_syslog(pamh, LOG_ERR, "badly formed mail path [%s]", path_mail); retval = PAM_SERVICE_ERR; goto get_folder_cleanup; } ctrl |= PAM_HOME_MAIL; if (hashcount != 0) { pam_syslog(pamh, LOG_ERR, "cannot do hash= and home directory mail"); } } } else { path = DEFAULT_MAIL_DIRECTORY; } /* put folder together */ hashcount = hashcount < strlen(user) ? hashcount : strlen(user); retval = PAM_BUF_ERR; if (ctrl & PAM_HOME_MAIL) { if (asprintf(&folder, MAIL_FILE_FORMAT, pwd->pw_dir, "", path) < 0) goto get_folder_cleanup; } else { int rc; size_t i; char *hash; if ((hash = malloc(2 * hashcount + 1)) == NULL) goto get_folder_cleanup; for (i = 0; i < hashcount; i++) { hash[2 * i] = '/'; hash[2 * i + 1] = user[i]; } hash[2 * i] = '\0'; rc = asprintf(&folder, MAIL_FILE_FORMAT, path, hash, user); _pam_overwrite(hash); _pam_drop(hash); if (rc < 0) goto get_folder_cleanup; } D(("folder=[%s]", folder)); retval = PAM_SUCCESS; /* tidy up */ get_folder_cleanup: user = NULL; path = NULL; *folder_p = folder; folder = NULL; if (retval == PAM_BUF_ERR) pam_syslog(pamh, LOG_CRIT, "out of memory for mail folder"); return retval; }