From 4d6f4ebe04b1c953cd31b8ed313eed193b29fc01 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Thu, 7 May 2009 09:48:57 +0200 Subject: cleanup and fixes for pam_sss - if PAM_USER==root return PAM_USER_UNKNOWN - pam_sss now can handle to following options: - use_first_pass: forces the module to use a previous stacked modules password and will never prompt the user - use_authtok: when password changing enforce the module to set the new password to the one provided by a previously stacked password module - forward_pass: store the passwords collected by the module as pam items for modules called later in the stack --- sss_client/pam_sss.c | 542 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 352 insertions(+), 190 deletions(-) (limited to 'sss_client') diff --git a/sss_client/pam_sss.c b/sss_client/pam_sss.c index cacb19b0d..4cc4ba974 100644 --- a/sss_client/pam_sss.c +++ b/sss_client/pam_sss.c @@ -13,6 +13,10 @@ #include "sss_cli.h" +#define FLAGS_USE_FIRST_PASS (1 << 0) +#define FLAGS_FORWARD_PASS (1 << 1) +#define FLAGS_USE_AUTHTOK (1 << 2) + struct pam_items { const char* pam_service; const char* pam_user; @@ -23,27 +27,211 @@ struct pam_items { const char* pam_newauthtok; const char* pamstack_authtok; const char* pamstack_oldauthtok; - int pam_service_size; - int pam_user_size; - int pam_tty_size; - int pam_ruser_size; - int pam_rhost_size; + size_t pam_service_size; + size_t pam_user_size; + size_t pam_tty_size; + size_t pam_ruser_size; + size_t pam_rhost_size; int pam_authtok_type; - int pam_authtok_size; + size_t pam_authtok_size; int pam_newauthtok_type; - int pam_newauthtok_size; + size_t pam_newauthtok_size; +}; + +static int pack_message(struct pam_items *pi, size_t *size, uint8_t **buffer) { + int len; + uint8_t *buf; + int rp; + + len = pi->pam_user_size + + pi->pam_service_size + + pi->pam_tty_size + + pi->pam_ruser_size + + pi->pam_rhost_size + + 2*sizeof(uint32_t) + pi->pam_authtok_size + + 2*sizeof(uint32_t) + pi->pam_newauthtok_size + + sizeof(uint32_t); + + buf = malloc(len); + if (buf == NULL) { + D(("malloc failed.")); + return PAM_BUF_ERR; + } + + memcpy(buf, pi->pam_user, pi->pam_user_size); + rp = pi->pam_user_size; + + memcpy(&buf[rp], pi->pam_service, pi->pam_service_size); + rp += pi->pam_service_size; + + memcpy(&buf[rp], pi->pam_tty, pi->pam_tty_size); + rp += pi->pam_tty_size; + + memcpy(&buf[rp], pi->pam_ruser, pi->pam_ruser_size); + rp += pi->pam_ruser_size; + + memcpy(&buf[rp], pi->pam_rhost, pi->pam_rhost_size); + rp += pi->pam_rhost_size; + + ((uint32_t *)(&buf[rp]))[0] = pi->pam_authtok_type; + rp += sizeof(uint32_t); + ((uint32_t *)(&buf[rp]))[0] = pi->pam_authtok_size; + rp += sizeof(uint32_t); + memcpy(&buf[rp], pi->pam_authtok, pi->pam_authtok_size); + rp += pi->pam_authtok_size; + _pam_overwrite((void *)pi->pam_authtok); + free((void *)pi->pam_authtok); + pi->pam_authtok = NULL; + + ((uint32_t *)(&buf[rp]))[0] = pi->pam_newauthtok_type; + rp += sizeof(uint32_t); + ((uint32_t *)(&buf[rp]))[0] = pi->pam_newauthtok_size; + rp += sizeof(uint32_t); + memcpy(&buf[rp], pi->pam_newauthtok, pi->pam_newauthtok_size); + rp += pi->pam_newauthtok_size; + _pam_overwrite((void *)pi->pam_newauthtok); + free((void *)pi->pam_newauthtok); + pi->pam_newauthtok = NULL; + + ((uint32_t *)(&buf[rp]))[0] = END_OF_PAM_REQUEST; + rp += sizeof(uint32_t); + + if (rp != len) { + D(("error during packet creation.")); + return PAM_BUF_ERR; + } + + *size = len; + *buffer = buf; + + return 0; +} + +static int null_strcmp(const char *s1, const char *s2) { + if (s1 == NULL && s2 == NULL) return 0; + if (s1 == NULL && s2 != NULL) return -1; + if (s1 != NULL && s2 == NULL) return 1; + return strcmp(s1, s2); +} + +enum { + PAM_CONV_DONE = 0, + PAM_CONV_STD, + PAM_CONV_REENTER, }; -static int eval_response(pam_handle_t *pamh, int buflen, uint8_t *buf) +static int do_pam_conversation(pam_handle_t *pamh, const int msg_style, + const char *msg, + const char *reenter_msg, + char **answer) +{ + int ret; + int state = PAM_CONV_STD; + struct pam_conv *conv; + struct pam_message *mesg[1]; + struct pam_response *resp=NULL; + + if ((msg_style == PAM_TEXT_INFO || msg_style == PAM_ERROR_MSG) && + msg == NULL) return PAM_SYSTEM_ERR; + + if ((msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON) && + (msg == NULL || answer == NULL)) return PAM_SYSTEM_ERR; + + ret=pam_get_item(pamh, PAM_CONV, (const void **) &conv); + if (ret != PAM_SUCCESS) return ret; + + do { + mesg[0] = malloc(sizeof(struct pam_message)); + if (mesg[0] == NULL) { + D(("Malloc failed.")); + return PAM_SYSTEM_ERR; + } + + mesg[0]->msg_style = msg_style; + if (state == PAM_CONV_REENTER) { + mesg[0]->msg = reenter_msg; + } else { + mesg[0]->msg = msg; + } + + ret=conv->conv(1, (const struct pam_message **) mesg, &resp, + conv->appdata_ptr); + free(mesg[0]); + if (ret != PAM_SUCCESS) { + D(("Conversation failure: %s.", pam_strerror(pamh,ret))); + return ret; + } + + if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON) { + if (resp == NULL) { + D(("response expected, but resp==NULL")); + return PAM_SYSTEM_ERR; + } + + if (state == PAM_CONV_REENTER) { + if (null_strcmp(*answer, resp[0].resp) != 0) { + D(("Passwords do not match.")); + _pam_overwrite((void *)resp[0].resp); + free(resp[0].resp); + if (*answer != NULL) { + _pam_overwrite((void *)*answer); + free(*answer); + *answer = NULL; + } + ret = do_pam_conversation(pamh, PAM_ERROR_MSG, + "Passwords do not match", NULL, + NULL); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return PAM_SYSTEM_ERR; + } + return PAM_CRED_ERR; + } + _pam_overwrite((void *)resp[0].resp); + free(resp[0].resp); + } else { + if (resp[0].resp == NULL) { + D(("Empty password")); + *answer = NULL; + } else { + *answer = strdup(resp[0].resp); + _pam_overwrite((void *)resp[0].resp); + free(resp[0].resp); + if(*answer == NULL) { + D(("strdup failed")); + return PAM_BUF_ERR; + } + } + } + free(resp); + resp = NULL; + } + + if (reenter_msg != NULL && state == PAM_CONV_STD) { + state = PAM_CONV_REENTER; + } else { + state = PAM_CONV_DONE; + } + } while (state != PAM_CONV_DONE); + + return PAM_SUCCESS; +} + +static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf) { int ret; - int p=0; + size_t p=0; char *env_item; int32_t *c; int32_t *type; int32_t *len; int32_t *pam_status; + if (buflen < (2*sizeof(int32_t))) { + D(("response buffer is too small")); + return PAM_BUF_ERR; + } + pam_status = ((int32_t *)(buf+p)); p += sizeof(int32_t); @@ -52,13 +240,34 @@ static int eval_response(pam_handle_t *pamh, int buflen, uint8_t *buf) p += sizeof(int32_t); while(*c>0) { + if (buflen < (p+2*sizeof(int32_t))) { + D(("response buffer is too small")); + return PAM_BUF_ERR; + } + type = ((int32_t *)(buf+p)); p += sizeof(int32_t); + len = ((int32_t *)(buf+p)); p += sizeof(int32_t); + + if (buflen < (p + *len)) { + D(("response buffer is too small")); + return PAM_BUF_ERR; + } + switch(*type) { case PAM_USER_INFO: + if (buf[p + (*len -1)] != '\0') { + D(("user info does not end with \\0.")); + break; + } D(("user info: [%s]", &buf[p])); + ret = do_pam_conversation(pamh, PAM_USER_INFO, (char *) &buf[p], + NULL, NULL); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation, canot display user info.")); + } break; case PAM_DOMAIN_NAME: D(("domain name: [%s]", &buf[p])); @@ -67,7 +276,7 @@ static int eval_response(pam_handle_t *pamh, int buflen, uint8_t *buf) case PAM_ENV_ITEM: case ALL_ENV_ITEM: if (buf[p + (*len -1)] != '\0') { - D(("env item does not end with \\0.\n")); + D(("env item does not end with \\0.")); break; } @@ -75,7 +284,7 @@ static int eval_response(pam_handle_t *pamh, int buflen, uint8_t *buf) if (*type == PAM_ENV_ITEM || *type == ALL_ENV_ITEM) { ret = pam_putenv(pamh, (char *)&buf[p]); if (ret != PAM_SUCCESS) { - D(("pam_putenv failed.\n")); + D(("pam_putenv failed.")); break; } } @@ -83,12 +292,12 @@ static int eval_response(pam_handle_t *pamh, int buflen, uint8_t *buf) if (*type == ENV_ITEM || *type == ALL_ENV_ITEM) { env_item = strdup((char *)&buf[p]); if (env_item == NULL) { - D(("strdup failed\n")); + D(("strdup failed")); break; } ret = putenv(env_item); if (ret == -1) { - D(("putenv failed.\n")); + D(("putenv failed.")); break; } } @@ -99,13 +308,20 @@ static int eval_response(pam_handle_t *pamh, int buflen, uint8_t *buf) --(*c); } - return 0; + return PAM_SUCCESS; } static int get_pam_items(pam_handle_t *pamh, struct pam_items *pi) { int ret; + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_EMPTY; + pi->pam_authtok = NULL; + pi->pam_authtok_size = 0; + pi->pam_newauthtok_type = SSS_AUTHTOK_TYPE_EMPTY; + pi->pam_newauthtok = NULL; + pi->pam_newauthtok_size = 0; + ret = pam_get_item(pamh, PAM_SERVICE, (const void **) &(pi->pam_service)); if (ret != PAM_SUCCESS) return ret; if (pi->pam_service == NULL) pi->pam_service=""; @@ -159,40 +375,36 @@ static void print_pam_items(struct pam_items pi) } } -static int pam_sss(int task, pam_handle_t *pamh, int flags, int argc, +static int pam_sss(int task, pam_handle_t *pamh, int pam_flags, int argc, const char **argv) { int ret; int errnop; - int c; struct pam_items pi; struct sss_cli_req_data rd; + uint8_t *buf=NULL; uint8_t *repbuf=NULL; size_t replen; - size_t rp; - char *buf=NULL; - struct pam_conv *conv; - struct pam_message *mesg[1]; - struct pam_response *resp=NULL; int pam_status; - char *newpwd[2]; - int forward_pass = 0; - int use_first_pass = 0; + uint32_t flags = 0; + char *answer; D(("Hello pam_sssd: %d", task)); for (; argc-- > 0; ++argv) { if (strcmp(*argv, "forward_pass") == 0) { - forward_pass = 1; + flags |= FLAGS_FORWARD_PASS; } else if (strcmp(*argv, "use_first_pass") == 0) { - use_first_pass = 1; + flags |= FLAGS_USE_FIRST_PASS; + } else if (strcmp(*argv, "use_authtok") == 0) { + flags |= FLAGS_USE_AUTHTOK; } else { D(("unknown option: %s", *argv)); } } /* TODO: add useful prelim check */ - if (task == SSS_PAM_CHAUTHTOK && (flags & PAM_PRELIM_CHECK)) { + if (task == SSS_PAM_CHAUTHTOK && (pam_flags & PAM_PRELIM_CHECK)) { D(("ignoring PAM_PRELIM_CHECK")); return PAM_SUCCESS; } @@ -203,210 +415,160 @@ static int pam_sss(int task, pam_handle_t *pamh, int flags, int argc, return ret; } - if (use_first_pass != 0 && - (*pi.pamstack_authtok != '\0' || *pi.pamstack_oldauthtok != '\0') && - (task == SSS_PAM_AUTHENTICATE || task == SSS_PAM_CHAUTHTOK)) { - pi.pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; - pi.pam_authtok = strdup(pi.pamstack_authtok); - pi.pam_authtok_size = strlen(pi.pamstack_authtok); - pi.pam_newauthtok_type = SSS_AUTHTOK_TYPE_PASSWORD; - pi.pam_newauthtok = strdup(pi.pamstack_oldauthtok); - pi.pam_newauthtok_size = strlen(pi.pamstack_oldauthtok); - - } else { - pi.pam_authtok_type = SSS_AUTHTOK_TYPE_EMPTY; - pi.pam_authtok = NULL; - pi.pam_authtok_size = 0; - pi.pam_newauthtok_type = SSS_AUTHTOK_TYPE_EMPTY; - pi.pam_newauthtok = NULL; - pi.pam_newauthtok_size = 0; - /* according to pam_conv(3) only one message should be requested by - * conv to keep compatibility to Solaris. Therefore we make separate - * calls to request AUTHTOK and OLDAUTHTOK. */ - if ( task == SSS_PAM_AUTHENTICATE || - (task == SSS_PAM_CHAUTHTOK && getuid() != 0)) { - ret=pam_get_item(pamh, PAM_CONV, (const void **) &conv); - if (ret != PAM_SUCCESS) return ret; - - mesg[0] = malloc(sizeof(struct pam_message)); - if (mesg[0] == NULL) { - D(("Malloc failed.\n")); + if (*pi.pam_user == '\0') { + D(("No user found, aborting.")); + return PAM_BAD_ITEM; + } + + if (strcmp(pi.pam_user, "root") == 0) { + D(("pam_sss will not handle root.")); + return PAM_USER_UNKNOWN; + } + + if (task == SSS_PAM_AUTHENTICATE || + (task == SSS_PAM_CHAUTHTOK && getuid() != 0)) { + if (flags & FLAGS_USE_FIRST_PASS) { + pi.pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + if (task == SSS_PAM_AUTHENTICATE) { + pi.pam_authtok = strdup(pi.pamstack_authtok); + } else if (task == SSS_PAM_CHAUTHTOK) { + pi.pam_authtok = strdup(pi.pamstack_oldauthtok); + } else { + D(("internal logic error")); return PAM_SYSTEM_ERR; } - mesg[0]->msg_style = PAM_PROMPT_ECHO_OFF; - mesg[0]->msg = strdup("Password: "); - - ret=conv->conv(1, (const struct pam_message **) mesg, &resp, - conv->appdata_ptr); - free((void *)mesg[0]->msg); - free(mesg[0]); + if (pi.pam_authtok == NULL) { + pam_status = PAM_BUF_ERR; + goto done; + } + pi.pam_authtok_size = strlen(pi.pam_authtok); + } else { + ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, "Password: ", + NULL, &answer); if (ret != PAM_SUCCESS) { - D(("Conversation failure: %s.\n", pam_strerror(pamh,ret))); + D(("do_pam_conversation failed.")); pam_status = ret; goto done; } - if (resp[0].resp == NULL) { - D(("Empty password\n")); + if (answer == NULL) { pi.pam_authtok = NULL; pi.pam_authtok_type = SSS_AUTHTOK_TYPE_EMPTY; + pi.pam_authtok_size=0; } else { - pi.pam_authtok = strdup(resp[0].resp); + pi.pam_authtok = strdup(answer); + _pam_overwrite((void *)answer); + free(answer); + answer=NULL; + if (pi.pam_authtok == NULL) { + pam_status = PAM_BUF_ERR; + goto done; + } pi.pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + pi.pam_authtok_size=strlen(pi.pam_authtok); } - pi.pam_authtok_size=strlen(pi.pam_authtok); - if (forward_pass != 0) { - ret = pam_set_item(pamh, PAM_AUTHTOK, resp[0].resp); + if (flags & FLAGS_FORWARD_PASS) { + if (task == SSS_PAM_AUTHENTICATE) { + ret = pam_set_item(pamh, PAM_AUTHTOK, pi.pam_authtok); + } else if (task == SSS_PAM_CHAUTHTOK) { + ret = pam_set_item(pamh, PAM_OLDAUTHTOK, pi.pam_authtok); + } else { + D(("internal logic error")); + return PAM_SYSTEM_ERR; + } if (ret != PAM_SUCCESS) { D(("Failed to set PAM_AUTHTOK, authtok may not be available for other modules")); } } } - - if (task == SSS_PAM_CHAUTHTOK) { - ret=pam_get_item(pamh, PAM_CONV, (const void **) &conv); - if (ret != PAM_SUCCESS) return ret; - - mesg[0] = malloc(sizeof(struct pam_message)); - if (mesg[0] == NULL) { - D(("Malloc failed.\n")); - return PAM_SYSTEM_ERR; + } + + if (task == SSS_PAM_CHAUTHTOK) { + if (flags & FLAGS_USE_AUTHTOK) { + pi.pam_newauthtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + pi.pam_newauthtok = strdup(pi.pamstack_authtok); + if (pi.pam_newauthtok == NULL) { + pam_status = PAM_BUF_ERR; + goto done; } - mesg[0]->msg_style = PAM_PROMPT_ECHO_OFF; - mesg[0]->msg = strdup("New Password: "); - - c = 0; - do { - ret=conv->conv(1, (const struct pam_message **) mesg, &resp, - conv->appdata_ptr); - free((void *)mesg[0]->msg); - if (ret != PAM_SUCCESS) { - D(("Conversation failure: %s.\n", pam_strerror(pamh,ret))); - pam_status = ret; - goto done; - } - - newpwd[c++] = strdup(resp[0].resp); - _pam_overwrite((void *)resp[0].resp); - free(resp[0].resp); - free(resp); - resp = NULL; - - mesg[0]->msg = strdup("Reenter new password: "); - } while(c < 2); - free(mesg[0]); - - if (strcmp(newpwd[0],newpwd[1]) != 0) { - pam_status = PAM_AUTHTOK_ERR; + pi.pam_newauthtok_size = strlen(pi.pam_newauthtok); + } else { + ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, + "New Password: ", + "Reenter new Password: ", + &answer); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + pam_status = ret; goto done; } - - if (newpwd[0] == NULL) { - D(("Empty password\n")); + if (answer == NULL) { pi.pam_newauthtok = NULL; pi.pam_newauthtok_type = SSS_AUTHTOK_TYPE_EMPTY; + pi.pam_newauthtok_size=0; } else { - pi.pam_newauthtok = strdup(newpwd[0]); + pi.pam_newauthtok = strdup(answer); + _pam_overwrite((void *)answer); + free(answer); + answer=NULL; + if (pi.pam_newauthtok == NULL) { + pam_status = PAM_BUF_ERR; + goto done; + } pi.pam_newauthtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + pi.pam_newauthtok_size=strlen(pi.pam_authtok); } - pi.pam_newauthtok_size=strlen(pi.pam_newauthtok); - _pam_overwrite((void *)newpwd[0]); - free(newpwd[0]); - _pam_overwrite((void *)newpwd[1]); - free(newpwd[1]); + if (flags & FLAGS_FORWARD_PASS) { + ret = pam_set_item(pamh, PAM_AUTHTOK, pi.pam_newauthtok); + if (ret != PAM_SUCCESS) { + D(("Failed to set PAM_AUTHTOK, authtok may not be available for other modules")); + } + } } } print_pam_items(pi); - if (pi.pam_user) { - rd.len = pi.pam_user_size + - pi.pam_service_size + - pi.pam_tty_size + - pi.pam_ruser_size + - pi.pam_rhost_size + - 2*sizeof(uint32_t) + pi.pam_authtok_size + - 2*sizeof(uint32_t) + pi.pam_newauthtok_size + - sizeof(uint32_t); - buf = malloc(rd.len); - - memcpy(buf, pi.pam_user, pi.pam_user_size); - rp = pi.pam_user_size; - - memcpy(&buf[rp], pi.pam_service, pi.pam_service_size); - rp += pi.pam_service_size; - - memcpy(&buf[rp], pi.pam_tty, pi.pam_tty_size); - rp += pi.pam_tty_size; - - memcpy(&buf[rp], pi.pam_ruser, pi.pam_ruser_size); - rp += pi.pam_ruser_size; - - memcpy(&buf[rp], pi.pam_rhost, pi.pam_rhost_size); - rp += pi.pam_rhost_size; - - ((uint32_t *)(&buf[rp]))[0] = pi.pam_authtok_type; - rp += sizeof(uint32_t); - ((uint32_t *)(&buf[rp]))[0] = pi.pam_authtok_size; - rp += sizeof(uint32_t); - memcpy(&buf[rp], pi.pam_authtok, pi.pam_authtok_size); - rp += pi.pam_authtok_size; - _pam_overwrite((void *)pi.pam_authtok); - free((void *)pi.pam_authtok); - - ((uint32_t *)(&buf[rp]))[0] = pi.pam_newauthtok_type; - rp += sizeof(uint32_t); - ((uint32_t *)(&buf[rp]))[0] = pi.pam_newauthtok_size; - rp += sizeof(uint32_t); - memcpy(&buf[rp], pi.pam_newauthtok, pi.pam_newauthtok_size); - rp += pi.pam_newauthtok_size; - _pam_overwrite((void *)pi.pam_newauthtok); - free((void *)pi.pam_newauthtok); - - ((uint32_t *)(&buf[rp]))[0] = END_OF_PAM_REQUEST; - rp += sizeof(uint32_t); - - if (rp != rd.len) { - D(("error during packet creation.")); - pam_status = PAM_ABORT; - goto done; - } - rd.data = buf; + ret = pack_message(&pi, &rd.len, &buf); + if (ret != 0) { + D(("pack_message failed.")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + rd.data = buf; - ret = sss_pam_make_request(task, &rd, &repbuf, &replen, &errnop); + ret = sss_pam_make_request(task, &rd, &repbuf, &replen, &errnop); - if (ret != NSS_STATUS_SUCCESS) { - D(("sss_pam_make_request failed.")); - pam_status = ret; - goto done; - } + if (ret != NSS_STATUS_SUCCESS) { + D(("sss_pam_make_request failed.")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } /* FIXME: add an end signature */ - if (replen