Method 1: I don't like simply because in 2 seconds of thinking about it those comments are essentially correct. You're creating a surface that exposes your /etc/shadow
file, whether it's exploitable or not, I simply don't like that having to do that.
Method 2: is also bad. Encoding passwords in a file is just dumb. Passing them through a pipe seems equally dangerous.
Method 3: is probably the way I'd go, and I don't think you'd have to write your own solution from scratch. In a couple of minutes of googling I found this implementation that someone put together using the libpam API.
c implementation
excerpt of C implementation - pam.c
#include <security/pam_appl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct pam_response *reply;
int null_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) {
*resp = reply;
return PAM_SUCCESS;
}
static struct pam_conv conv = { null_conv, NULL };
int main(int argc, char *argv[]) {
int retval;
char *user, *pass;
if(argc == 3) {
user = argv[1];
pass = strdup(argv[2]);
} else {
fprintf(stderr, "Usage: login [username] [password]\n");
exit(1);
}
return authenticate("system-auth", user, pass);
}
int authenticate(char *service, char *user, char *pass) {
pam_handle_t *pamh = NULL;
int retval = pam_start(service, user, &conv, &pamh);
if (retval == PAM_SUCCESS) {
reply = (struct pam_response *)malloc(sizeof(struct pam_response));
reply[0].resp = pass;
reply[0].resp_retcode = 0;
retval = pam_authenticate(pamh, 0);
if (retval == PAM_SUCCESS)
fprintf(stdout, "Authenticated\n");
else
fprintf(stdout, "Not Authenticated\n");
pam_end(pamh, PAM_SUCCESS);
return ( retval == PAM_SUCCESS ? 0:1 );
}
return ( retval == PAM_SUCCESS ? 0:1 );
}
command to compile it
$ gcc -g -lpam -o chkpasswd pam.c
example run
$ ./chkpasswd myusername mypassword
I would imagine that with a little effort this approach could be adapted to suit your needs with Horde.
PHP implmentation
As another alternative to this approach you could potentially roll your own in PHP. I found this PAM library on the PECL website that looks like what you'd want.
I'd also take a look at how the Moodle project does it. It's discussed here.
PAM from a security perspective
Looking at the purpose of PAM I would expect that the API interface was designed so that no access to lower level entities, such as the /etc/shadow
, would need to be required so that users of the API can use it. This is discussed a bit on the Gentoo wiki in this article titled: PAM, section: How PAM works.
exerpt from How PAM works
So when a user wants to authenticate itself against, say, a web application, then this web application calls PAM (passing on the user id and perhaps password or challenge) and checks the PAM return to see if the user is authenticated and allowed access to the application. It is PAMs task underlyingly to see where to authenticate against (such as a central database or LDAP server).
The strength of PAM is that everyone can build PAM modules to integrate with any PAM-enabled service or application. If a company releases a new service for authentication, all it needs to do is provide a PAM module that interacts with its service, and then all software that uses PAM can work with this service immediately: no need to rebuild or enhance those software titles.
Method 4: Also a good alternative. There should be good accessibility to libraries and such to make the calls necessary to access something like LDAP over the wire to do your authentication for you. An LDAP server could also be setup on the same system as the Horde installation, configuring it for just Horde's use.
This would gain you access to the underlying system's authentication, potentially, by "wrapping" it in an LDAP service for Horde to consume.
References
With bash
or zsh
:
unset -v password # make sure it's not exported
set +o allexport # make sure variables are not automatically exported
IFS= read -rs password < /dev/tty &&
printf '{"username":"myname","password":"%s"}\n' "$password" | cmd
Without IFS=
, read
would strip leading and trailing blanks from the password you type.
Without -r
, it would process backslashes as a quoting character.
You want to make sure you only ever read from the terminal.
echo
can't be used reliably. In bash
and zsh
, printf
is builtin so that command line wouldn't show in the output of ps
.
In bash
, you need to quote $password
as otherwise the split+glob operator is applied to it.
That's still wrong though as you'd need to encode that string as JSON. For instance, double-quote and backslash at least would be a problem. You probably need to worry about the encoding of those characters. Does your program expect UTF-8 strings? What does your terminal send?
To add a prompt string, with zsh
:
IFS= read -rs 'password?Please enter a password: '
With bash
:
IFS= read -rsp 'Please enter a password: ' password
Best Answer
Elaborated on here (via):
[End-of-lifed: 2009. Anyone know about more recent AIX?]
...
[Release year: 2012]