It was blessed by POSIX.1-2001, and GCC says that it won't go away, possibly ever. memset(3) is dangerous, as the 2nd and 3rd arguments can be accidentally swapped --who remembers what's the order of the 2nd and 3rd parameters to memset(3) without checking the manual page or some code that uses it?--. Some recent compilers may be able to catch that via some warnings, but those are not infalible. And even if compiler warnings could always catch that, the time lost in fixing or checking the docs is lost for no clear gain. Having a sane API that is unambiguous is the Right Thing (tm); and that API is bzero(3). If someone doesn't believe memset(3) is error-prone, please read the book "Unix Network Programming", Volume 1, 3rd Edition by Stevens, et al., Section 1.2. See a stackoverflow reference in the link below[1]. bzero(3) had a bad fame in the bad old days, because some ancient systems (I'm talking of many decades ago) shipped a broken version of bzero(3). We can assume that all systems in which current shadow utils can be built, have a working version of bzero(3) --if not, please fix your broken system; don't blame the programmer--. One reason that some use today to avoid bzero(3) in favor of memset(3) is that memset(3) is more often used; but that's a circular reasoning. Even if bzero(3) wasn't supported by the system, it would need to be invented. It's the right API. Another reason that some argue is that POSIX.1-2008 removed the specification of bzero(3). That's not a problem, because GCC will probably support it forever, and even if it didn't, we can redefine it like we do with memzero(). bzero(3) is just a one-liner wrapper around memset(3). Link: [1] <https://stackoverflow.com/a/17097978> Cc: Christian Göttsche <cgzones@googlemail.com> Cc: Serge Hallyn <serge@hallyn.com> Cc: Iker Pedrosa <ipedrosa@redhat.com> Signed-off-by: Alejandro Colomar <alx@kernel.org>
149 lines
3.5 KiB
C
149 lines
3.5 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2009 - 2010, Nicolas François
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#ident "$Id:$"
|
|
|
|
#ifdef USE_PAM
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <strings.h>
|
|
|
|
#include <security/pam_appl.h>
|
|
|
|
#include "alloc.h"
|
|
#include "prototypes.h"
|
|
#include "shadowlog.h"
|
|
|
|
/*@null@*/ /*@only@*/static const char *non_interactive_password = NULL;
|
|
static int ni_conv (int num_msg,
|
|
const struct pam_message **msg,
|
|
struct pam_response **resp,
|
|
unused void *appdata_ptr);
|
|
static const struct pam_conv non_interactive_pam_conv = {
|
|
ni_conv,
|
|
NULL
|
|
};
|
|
|
|
|
|
|
|
static int ni_conv (int num_msg,
|
|
const struct pam_message **msg,
|
|
struct pam_response **resp,
|
|
unused void *appdata_ptr)
|
|
{
|
|
struct pam_response *responses;
|
|
int count;
|
|
|
|
assert (NULL != non_interactive_password);
|
|
|
|
if (num_msg <= 0) {
|
|
return PAM_CONV_ERR;
|
|
}
|
|
|
|
responses = CALLOC (num_msg, struct pam_response);
|
|
if (NULL == responses) {
|
|
return PAM_CONV_ERR;
|
|
}
|
|
|
|
for (count=0; count < num_msg; count++) {
|
|
responses[count].resp_retcode = 0;
|
|
|
|
switch (msg[count]->msg_style) {
|
|
case PAM_PROMPT_ECHO_ON:
|
|
fprintf (log_get_logfd(),
|
|
_("%s: PAM modules requesting echoing are not supported.\n"),
|
|
log_get_progname());
|
|
goto failed_conversation;
|
|
case PAM_PROMPT_ECHO_OFF:
|
|
responses[count].resp = strdup (non_interactive_password);
|
|
if (NULL == responses[count].resp) {
|
|
goto failed_conversation;
|
|
}
|
|
break;
|
|
case PAM_ERROR_MSG:
|
|
if ( (NULL == msg[count]->msg)
|
|
|| (fprintf (log_get_logfd(), "%s\n", msg[count]->msg) <0)) {
|
|
goto failed_conversation;
|
|
}
|
|
responses[count].resp = NULL;
|
|
break;
|
|
case PAM_TEXT_INFO:
|
|
if ( (NULL == msg[count]->msg)
|
|
|| (fprintf (stdout, "%s\n", msg[count]->msg) <0)) {
|
|
goto failed_conversation;
|
|
}
|
|
responses[count].resp = NULL;
|
|
break;
|
|
default:
|
|
(void) fprintf (log_get_logfd(),
|
|
_("%s: conversation type %d not supported.\n"),
|
|
log_get_progname(), msg[count]->msg_style);
|
|
goto failed_conversation;
|
|
}
|
|
}
|
|
|
|
*resp = responses;
|
|
|
|
return PAM_SUCCESS;
|
|
|
|
failed_conversation:
|
|
for (count=0; count < num_msg; count++) {
|
|
if (NULL != responses[count].resp) {
|
|
bzero(responses[count].resp,
|
|
strlen(responses[count].resp));
|
|
free (responses[count].resp);
|
|
responses[count].resp = NULL;
|
|
}
|
|
}
|
|
|
|
free (responses);
|
|
*resp = NULL;
|
|
|
|
return PAM_CONV_ERR;
|
|
}
|
|
|
|
|
|
/*
|
|
* Change non interactively the user's password using PAM.
|
|
*
|
|
* Return 0 on success, 1 on failure.
|
|
*/
|
|
int do_pam_passwd_non_interactive (const char *pam_service,
|
|
const char *username,
|
|
const char* password)
|
|
{
|
|
pam_handle_t *pamh = NULL;
|
|
int ret;
|
|
|
|
ret = pam_start (pam_service, username, &non_interactive_pam_conv, &pamh);
|
|
if (ret != PAM_SUCCESS) {
|
|
fprintf (log_get_logfd(),
|
|
_("%s: (user %s) pam_start failure %d\n"),
|
|
log_get_progname(), username, ret);
|
|
return 1;
|
|
}
|
|
|
|
non_interactive_password = password;
|
|
ret = pam_chauthtok (pamh, 0);
|
|
if (ret != PAM_SUCCESS) {
|
|
fprintf (log_get_logfd(),
|
|
_("%s: (user %s) pam_chauthtok() failed, error:\n"
|
|
"%s\n"),
|
|
log_get_progname(), username, pam_strerror (pamh, ret));
|
|
}
|
|
|
|
(void) pam_end (pamh, PAM_SUCCESS);
|
|
|
|
return ((PAM_SUCCESS == ret) ? 0 : 1);
|
|
}
|
|
#else /* !USE_PAM */
|
|
extern int ISO_C_forbids_an_empty_translation_unit;
|
|
#endif /* !USE_PAM */
|