1
0
This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
TP-Link_Archer-XR500v/EN7526G_3.18Kernel_SDK/apps/public/ftpd/pam.c
2024-07-22 01:58:46 -03:00

235 lines
5.3 KiB
C
Executable File

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "extern.h"
#ifdef HAVE_SECURITY_PAM_APPL_H
#include <security/pam_appl.h>
#endif
#ifndef PAM_CONV_AGAIN
# define PAM_CONV_AGAIN PAM_TRY_AGAIN
#endif
#ifndef PAM_INCOMPLETE
# define PAM_INCOMPLETE PAM_TRY_AGAIN
#endif
#ifdef WITH_PAM
static int PAM_conv __P ((int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr));
/* FIXME: We still have a side effect since we use the global variable
cred. A better approach would be to use the pcred parameter
in pam_user(). */
static struct pam_conv PAM_conversation = { &PAM_conv, &cred };
/* PAM authentication, now using the PAM's async feature. */
static pam_handle_t *pamh;
static int
PAM_conv (int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr)
{
struct pam_response *repl = NULL;
int retval, count = 0, replies = 0;
int size = sizeof(struct pam_response);
struct credentials *pcred = (struct credentials *) appdata_ptr;
#define GET_MEM \
if (!(repl = realloc (repl, size))) \
return PAM_CONV_ERR; \
size += sizeof (struct pam_response)
retval = PAM_SUCCESS;
for (count = 0; count < num_msg; count++)
{
int savemsg = 0;
switch (msg[count]->msg_style)
{
case PAM_PROMPT_ECHO_ON:
GET_MEM;
repl[replies].resp_retcode = PAM_SUCCESS;
repl[replies].resp = sgetsave (pcred->name);
replies++;
break;
case PAM_PROMPT_ECHO_OFF:
GET_MEM;
if (pcred->pass == 0)
{
savemsg = 1;
retval = PAM_CONV_AGAIN;
}
else
{
repl[replies].resp_retcode = PAM_SUCCESS;
repl[replies].resp = sgetsave (pcred->pass);
replies++;
}
break;
case PAM_TEXT_INFO:
savemsg = 1;
break;
case PAM_ERROR_MSG:
default:
/* Must be an error of some sort... */
savemsg = 1;
retval = PAM_CONV_ERR;
}
if (savemsg)
{
/* FIXME: This is a serious problem. If the PAM message
is multilines, the reply _must_ be formated correctly.
The way to do this would be to consider \n as a boundary then
in the ftpd.c:user() or ftpd.c:pass() check for it and send
a lreply(). But I'm not sure the RFCs allow mutilines replies
for a passwd challenge. Many clients will simply break. */
if (pcred->message) /* XXX: make sure we split newlines correctly */
{
size_t len = strlen (pcred->message);
char *s = realloc (pcred->message, len
+ strlen (msg[count]->msg) + 1);
if (s == NULL)
{
free (pcred->message);
pcred->message = NULL;
}
else
{
pcred->message = s;
strcat (pcred->message, msg[count]->msg);
}
}
else
pcred->message = sgetsave (msg[count]->msg);
if (pcred->message == NULL)
retval = PAM_CONV_ERR;
else
{
char *sp;
/* FIXME: What's this for ? */
/* Remove trailing `: ' */
sp = pcred->message + strlen (pcred->message);
while (sp > pcred->message && strchr (" \t\n:", *--sp))
*sp = '\0';
}
}
/* In case of error, drop responses and return */
if (retval)
{
/* FIXME: drop_reply is not standard, need to clean this. */
//_pam_drop_reply (repl, replies);
free (repl);
return retval;
}
}
if (repl)
*resp = repl;
return PAM_SUCCESS;
}
/* Non-zero means failure. */
static int
pam_doit (struct credentials *pcred)
{
char *username;
int error;
error = pam_authenticate (pamh, 0);
/* Probably being call for the passwd. */
if (error == PAM_CONV_AGAIN || error == PAM_INCOMPLETE)
{
/* Avoid overly terse passwd messages and let the people
upstairs do something sane. */
if (pcred->message && !strcasecmp (pcred->message, "password"))
{
free (pcred->message);
pcred->message = NULL;
}
return 0;
}
if (error == PAM_SUCCESS) /* Alright, we got it */
{
error = pam_acct_mgmt (pamh, 0);
if (error == PAM_SUCCESS)
error = pam_setcred (pamh, PAM_ESTABLISH_CRED);
if (error == PAM_SUCCESS)
error = pam_get_item (pamh, PAM_USER, (const void **) &username);
if (error == PAM_SUCCESS)
{
if (sgetcred (username, pcred) != 0)
error = PAM_AUTH_ERR;
else
{
if (strcasecmp (username, "ftp") == 0)
pcred->guest = 1;
}
}
}
pam_end(pamh, error);
pamh = 0;
return (error != PAM_SUCCESS);
}
/* Non-zero return means failure. */
int
pam_user (const char *username, struct credentials *pcred)
{
int error;
if (pamh != 0)
{
pam_end (pamh, PAM_ABORT);
pamh = 0;
}
if (pcred->name)
free (pcred->name);
pcred->name = strdup (username);
if (pcred->message)
free (pcred->message);
pcred->message = NULL;
error = pam_start ("ftp", pcred->name, &PAM_conversation, &pamh);
if (error == PAM_SUCCESS)
error = pam_set_item (pamh, PAM_RHOST, pcred->remotehost);
if (error != PAM_SUCCESS)
{
pam_end (pamh, error);
pamh = 0;
}
if (pamh)
error = pam_doit (pcred);
return (error != PAM_SUCCESS);
}
/* Nonzero value return for error. */
int
pam_pass (const char *passwd, struct credentials *pcred)
{
int error = PAM_AUTH_ERR;
if (pamh)
{
pcred->pass = passwd;
error = pam_doit (pcred);
pcred->pass = NULL;
}
return error != PAM_SUCCESS;
}
#endif /* WITH_PAM */