/* $Id: agent_shared.c,v 1.86 2011/07/11 22:05:48 sbajic Exp $ */
/*
DSPAM
COPYRIGHT (C) 2002-2012 DSPAM PROJECT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
/*
* agent_shared.c - shared agent-based components
*
* DESCRIPTION
* agent-based components shared between the full dspam agent (dspam)
* and the lightweight client agent (dspamc)
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#ifdef HAVE_UNISTD_H
#include
#include
#endif
#include
#include
#include
#ifdef _WIN32
#include
#include
#define WIDEXITED(x) 1
#define WEXITSTATUS(x) (x)
#include
#else
#include
#include
#endif
#include "util.h"
#include "read_config.h"
#ifdef DAEMON
#include "daemon.h"
#include "dspam.h"
#endif
#ifdef TIME_WITH_SYS_TIME
# include
# include
#else
# ifdef HAVE_SYS_TIME_H
# include
# else
# include
# endif
#endif
#include "agent_shared.h"
#include "language.h"
#include "buffer.h"
char * __pw_name = NULL;
uid_t __pw_uid;
/*
* initialize_atx(AGENT_CTX *)
*
* DESCRIPTION
* initializes an existing agent context
*
* INPUT ARGUMENTS
* ATX agent context to initialize
*
* RETURN VALUES
* returns 0 on success
*/
int initialize_atx(AGENT_CTX *ATX) {
memset(ATX, 0, sizeof(AGENT_CTX));
ATX->training_buffer = 0;
ATX->train_pristine = 0;
ATX->classification = DSR_NONE;
ATX->source = DSS_NONE;
ATX->operating_mode = DSM_PROCESS;
ATX->fork = 1;
ATX->users = nt_create (NT_CHAR);
if (ATX->users == NULL) {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
return EUNKNOWN;
}
#ifdef TRUSTED_USER_SECURITY
if (!__pw_name) {
LOG(LOG_ERR, ERR_AGENT_RUNTIME_USER);
exit(EXIT_FAILURE);
}
LOGDEBUG("checking trusted user list for %s(%d)", __pw_name, __pw_uid);
if (__pw_uid == 0)
ATX->trusted = 1;
else
ATX->trusted = _ds_match_attribute(agent_config, "Trust", __pw_name);
if (!ATX->trusted)
nt_add (ATX->users, __pw_name);
#endif
return 0;
}
/*
* process_arguments(AGENT_CTX *, int argc, char *argv[])
*
* DESCRIPTION
* master commandline argument process loop
*
* INPUT ARGUMENTS
* ATX agent context
* argc number of arguments provided
* argv array of arguments
*
* RETURN VALUES
* returns 0 on success, EINVAL when invalid options specified
*/
int process_arguments(AGENT_CTX *ATX, int argc, char **argv) {
int flag_u = 0, flag_r = 0;
int client = (_ds_read_attribute(agent_config, "ClientHost") != NULL);
char *ptrptr;
int i;
#ifdef DEBUG
ATX->debug_args[0] = 0;
#endif
ATX->client_args[0] = 0;
for (i=0; idebug_args, argv[i], sizeof (ATX->debug_args));
strlcat (ATX->debug_args, " ", sizeof (ATX->debug_args));
#endif
/* Terminate user/rcpt lists */
if ((flag_u || flag_r) &&
(argv[i][0] == '-' || argv[i][0] == 0 || !strcmp(argv[i], "--")))
{
flag_u = flag_r = 0;
if (!strcmp(argv[i], "--"))
continue;
}
if (!strcmp (argv[i], "--user")) {
flag_u = 1;
continue;
}
if (!strcmp (argv[i], "--rcpt-to"))
{
if (!ATX->recipients) {
ATX->recipients = nt_create(NT_CHAR);
if (ATX->recipients == NULL) {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
return EUNKNOWN;
}
}
flag_r = 1;
continue;
}
/* Build arg list to pass to server (when in client/server mode) */
if (client && !flag_u && !flag_r && i>0)
{
if (argv[i][0] == 0)
strlcat(ATX->client_args, "\"", sizeof(ATX->client_args));
strlcat (ATX->client_args, argv[i], sizeof(ATX->client_args));
if (argv[i][0] == 0)
strlcat(ATX->client_args, "\"", sizeof(ATX->client_args));
strlcat (ATX->client_args, " ", sizeof(ATX->client_args));
}
if (!strcmp (argv[i], "--debug"))
{
#ifdef DEBUG
if (DO_DEBUG == 0)
DO_DEBUG = 1;
#endif
continue;
}
#if defined(DAEMON) && !defined(_DSPAMC_H)
if (!strcmp (argv[i], "--client")) {
ATX->client_mode = 1;
continue;
}
#ifdef TRUSTED_USER_SECURITY
if (!strcmp (argv[i], "--daemon") && ATX->trusted)
#else
if (!strcmp (argv[i], "--daemon"))
#endif
{
ATX->operating_mode = DSM_DAEMON;
continue;
}
#endif
if (!strcmp (argv[i], "--nofork")) {
ATX->fork = 0;
continue;
}
if (!strncmp (argv[i], "--mode=", 7))
{
char *mode = strchr(argv[i], '=')+1;
if (process_mode(ATX, mode))
return EINVAL;
ATX->flags |= DAF_FIXED_TR_MODE;
continue;
}
/* Build RCPT TO list */
if (flag_r)
{
if (argv[i] != NULL && strlen (argv[i]) < MAX_USERNAME_LENGTH)
{
char user[MAX_USERNAME_LENGTH];
if (_ds_match_attribute(agent_config, "Broken", "case"))
lc(user, argv[i]);
else
strcpy(user, argv[i]);
#ifdef TRUSTED_USER_SECURITY
if (!ATX->trusted && strcmp(user, __pw_name)) {
LOG(LOG_ERR, ERR_TRUSTED_USER, __pw_uid, __pw_name);
return EINVAL;
}
if (ATX->trusted) {
#endif
if (_ds_validate_address(user) == 1) {
nt_add (ATX->recipients, user);
} else {
LOG(LOG_ERR, "Invalid email address: %s", user);
return EINVAL;
}
#ifdef TRUSTED_USER_SECURITY
}
#endif
}
continue;
}
/* Build process user list */
if (flag_u)
{
if (argv[i] != NULL && strlen (argv[i]) < MAX_USERNAME_LENGTH)
{
if (strstr(argv[i], "../") != NULL || strstr(argv[i], "..\\") != NULL) {
LOG(LOG_ERR, "Illegal username ('../' or '..\\' not allowed in username)");
return EINVAL;
} else {
char user[MAX_USERNAME_LENGTH];
if (_ds_match_attribute(agent_config, "Broken", "case"))
lc(user, argv[i]);
else
strcpy(user, argv[i]);
#ifdef TRUSTED_USER_SECURITY
if (!ATX->trusted && strcmp(user, __pw_name)) {
LOG(LOG_ERR, ERR_TRUSTED_USER, __pw_uid, __pw_name);
return EINVAL;
}
if (ATX->trusted)
#endif
nt_add (ATX->users, user);
}
}
continue;
}
if (!strncmp (argv[i], "--mail-from=", 12))
{
strlcpy(ATX->mailfrom, strchr(argv[i], '=')+1, sizeof(ATX->mailfrom));
LOGDEBUG("MAIL FROM: %s", ATX->mailfrom);
continue;
}
if (!strncmp (argv[i], "--profile=", 10))
{
#ifdef TRUSTED_USER_SECURITY
if (!ATX->trusted) {
LOG(LOG_ERR, ERR_TRUSTED_PRIV, "--profile",
__pw_uid, __pw_name);
return EINVAL;
}
#endif
if (!_ds_match_attribute(agent_config, "Profile", argv[i]+10)) {
LOG(LOG_ERR,ERR_AGENT_NO_SUCH_PROFILE, argv[i]+10);
return EINVAL;
} else {
_ds_overwrite_attribute(agent_config, "DefaultProfile", argv[i]+10);
}
continue;
}
if (!strncmp (argv[i], "--signature=", 12))
{
strlcpy(ATX->signature, strchr(argv[i], '=')+1, sizeof(ATX->signature));
continue;
}
if (!strncmp (argv[i], "--class=", 8))
{
char *ptr = strchr(argv[i], '=')+1;
char *spam = _ds_read_attribute(agent_config, "ClassAliasSpam");
char *nonspam = _ds_read_attribute(agent_config, "ClassAliasNonspam");
if (!strcmp(ptr, "spam") || (spam && !strcmp(ptr, spam)))
{
ATX->classification = DSR_ISSPAM;
} else if (!strcmp(ptr, "innocent") || !strcmp(ptr, "nonspam") ||
(nonspam && !strcmp(ptr, nonspam)))
{
ATX->classification = DSR_ISINNOCENT;
}
else
{
LOG(LOG_ERR, ERR_AGENT_NO_SUCH_CLASS, ptr);
return EINVAL;
}
continue;
}
if (!strncmp (argv[i], "--source=", 9))
{
char *ptr = strchr(argv[i], '=')+1;
if (!strcmp(ptr, "corpus"))
ATX->source = DSS_CORPUS;
else if (!strcmp(ptr, "inoculation"))
ATX->source = DSS_INOCULATION;
else if (!strcmp(ptr, "error"))
ATX->source = DSS_ERROR;
else
{
LOG(LOG_ERR, ERR_AGENT_NO_SUCH_SOURCE, ptr);
return EINVAL;
}
continue;
}
if (!strcmp (argv[i], "--classify"))
{
ATX->operating_mode = DSM_CLASSIFY;
ATX->training_mode = DST_NOTRAIN;
continue;
}
if (!strcmp (argv[i], "--process"))
{
ATX->operating_mode = DSM_PROCESS;
continue;
}
if (!strncmp (argv[i], "--deliver=", 10))
{
char *dup = strdup(strchr(argv[i], '=')+1);
char *ptr;
if (dup == NULL) {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
return EUNKNOWN;
}
ptr = strtok_r(dup, ",", &ptrptr);
while(ptr != NULL) {
if (!strcmp(ptr, "stdout")) {
ATX->flags |= DAF_DELIVER_SPAM;
ATX->flags |= DAF_DELIVER_INNOCENT;
ATX->flags |= DAF_STDOUT;
}
else if (!strcmp(ptr, "spam"))
ATX->flags |= DAF_DELIVER_SPAM;
else if (!strcmp(ptr, "innocent") || !strcmp(ptr, "nonspam"))
ATX->flags |= DAF_DELIVER_INNOCENT;
else if (!strcmp(ptr, "summary"))
ATX->flags |= DAF_SUMMARY;
else
{
LOG(LOG_ERR, ERR_AGENT_NO_SUCH_DELIVER, ptr);
free(dup);
return EINVAL;
}
ptr = strtok_r(NULL, ",", &ptrptr);
}
free(dup);
continue;
}
if (!strncmp (argv[i], "--feature=", 10))
{
ATX->feature = 1;
process_features(ATX, strchr(argv[i], '=')+1);
continue;
}
if (!strcmp (argv[i], "--stdout"))
{
ATX->flags |= DAF_STDOUT;
continue;
}
if (!strcmp (argv[i], "--help"))
{
fprintf (stderr, "%s\n", SYNTAX);
exit(EXIT_SUCCESS);
}
if (!strcmp (argv[i], "--version"))
{
printf ("\nDSPAM Anti-Spam Suite %s (agent/library)\n\n", VERSION);
printf ("Copyright (C) 2002-2012 DSPAM Project\n");
printf ("http://dspam.sourceforge.net.\n\n");
printf ("DSPAM may be copied only under the terms of the GNU Affero General Public\n");
printf ("License, a copy of which can be found with the DSPAM distribution kit.\n\n");
#ifdef TRUSTED_USER_SECURITY
if (ATX->trusted) {
#endif
printf("Configuration parameters: %s\n\n", CONFIGURE_ARGS);
#ifdef TRUSTED_USER_SECURITY
}
#endif
exit (EXIT_SUCCESS);
}
/* Append all unknown arguments as mailer args */
if (i>0
#ifdef TRUSTED_USER_SECURITY
&& ATX->trusted
#endif
)
{
if (argv[i][0] == 0)
strlcat (ATX->mailer_args, "\"\"", sizeof (ATX->mailer_args));
else
strlcat (ATX->mailer_args, argv[i], sizeof (ATX->mailer_args));
strlcat (ATX->mailer_args, " ", sizeof (ATX->mailer_args));
}
}
return 0;
}
/*
* process_features(AGENT_CTX *, const char *)
*
* DESCRIPTION
* convert --feature= stdin into agent context values
*
* INPUT ARGUMENTS
* ATX agent context
* in remainder of --feature= stdin
*
* RETURN VALUES
* returns 0 on success, EINVAL when invalid options specified
*
*/
int process_features(AGENT_CTX *ATX, const char *in) {
char *ptr, *dup, *ptrptr;
int ret = 0;
if (!in || in[0]==0)
return 0;
dup = strdup(in);
if (dup == NULL) {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
return EUNKNOWN;
}
ptr = strtok_r(dup, ",", &ptrptr);
while(ptr != NULL) {
if (!strncmp(ptr, "no",2))
ATX->flags |= DAF_NOISE;
else if (!strncmp(ptr, "wh", 2))
ATX->flags |= DAF_WHITELIST;
else if (!strncmp(ptr, "tb=", 3)) {
ATX->training_buffer = atoi(strchr(ptr, '=')+1);
if (ATX->training_buffer < 0 || ATX->training_buffer > 10) {
LOG(LOG_ERR, ERR_AGENT_TB_INVALID);
ret = EINVAL;
}
}
else {
LOG(LOG_ERR, ERR_AGENT_NO_SUCH_FEATURE, ptr);
ret = EINVAL;
}
ptr = strtok_r(NULL, ",", &ptrptr);
}
free(dup);
return ret;
}
/*
* process_mode(AGENT_CTX *, const char *)
*
* DESCRIPTION
* convert --mode= stdin into training mode
*
* INPUT ARGUMENTS
* ATX agent context
* mode remainder of --mode= stdin
*
* RETURN VALUES
* returns 0 on success, EINVAL when invalid mode specified
*/
int process_mode(AGENT_CTX *ATX, const char *mode) {
if (!mode)
return EINVAL;
if (!strcmp(mode, "toe"))
ATX->training_mode = DST_TOE;
else if (!strcmp(mode, "teft"))
ATX->training_mode = DST_TEFT;
else if (!strcmp(mode, "tum"))
ATX->training_mode = DST_TUM;
else if (!strcmp(mode, "notrain"))
ATX->training_mode = DST_NOTRAIN;
else if (!strcmp(mode, "unlearn")) {
ATX->training_mode = DST_TEFT;
ATX->flags |= DAF_UNLEARN;
} else {
LOG(LOG_ERR, ERR_AGENT_TR_MODE_INVALID, mode);
return EINVAL;
}
return 0;
}
/*
* apply_defaults(AGENT_CTX *)
*
* DESCRIPTION
* apply default values from dspam.conf in absence of other options
*
* INPUT ARGUMENTS
* ATX agent context
*
* RETURN VALUES
* returns 0 on success
*/
int apply_defaults(AGENT_CTX *ATX) {
/* Training mode */
if (!(ATX->flags & DAF_FIXED_TR_MODE)) {
char *v = _ds_read_attribute(agent_config, "TrainingMode");
if (process_mode(ATX, v)) {
LOG(LOG_ERR, ERR_AGENT_NO_TR_MODE);
return EINVAL;
}
}
/* Default delivery agent */
if ( ! (ATX->flags & DAF_STDOUT)
&& ATX->operating_mode != DSM_CLASSIFY
&& (ATX->flags & DAF_DELIVER_INNOCENT || ATX->flags & DAF_DELIVER_SPAM))
{
char key[32];
#ifdef TRUSTED_USER_SECURITY
if (!ATX->trusted)
strcpy(key, "UntrustedDeliveryAgent");
else
#endif
strcpy(key, "TrustedDeliveryAgent");
char *value = _ds_read_attribute(agent_config, key);
if (value) {
char *trimmed_value = ALLTRIM(strdup(value));
if (trimmed_value && *trimmed_value == '\0') {
LOG(LOG_ERR, ERR_AGENT_NO_AGENT, key);
free(trimmed_value);
return EINVAL;
}
if (trimmed_value) free(trimmed_value);
char fmt[sizeof(ATX->mailer_args)];
snprintf(fmt, sizeof(fmt), "%s ", value);
#ifdef TRUSTED_USER_SECURITY
if (ATX->trusted)
#endif
strlcat(fmt, ATX->mailer_args, sizeof(fmt));
strcpy(ATX->mailer_args, fmt);
} else if (!_ds_read_attribute(agent_config, "DeliveryHost")) {
LOG(LOG_ERR, ERR_AGENT_NO_AGENT, key);
return EINVAL;
}
}
/* Default quarantine agent */
if (_ds_read_attribute(agent_config, "QuarantineAgent")) {
snprintf(ATX->spam_args, sizeof(ATX->spam_args), "%s ",
_ds_read_attribute(agent_config, "QuarantineAgent"));
} else {
LOGDEBUG("No QuarantineAgent option found. Using standard quarantine.");
}
/* Features */
if (!ATX->feature && _ds_find_attribute(agent_config, "Feature")) {
attribute_t attrib = _ds_find_attribute(agent_config, "Feature");
while(attrib != NULL) {
process_features(ATX, attrib->value);
attrib = attrib->next;
}
}
return 0;
}
/*
* check_configuration(AGENT_CTX *)
*
* DESCRIPTION
* sanity-check agent configuration
*
* INPUT ARGUMENTS
* ATX agent context
*
* RETURN VALUES
* returns 0 on success, EINVAL on invalid configuration
*/
int check_configuration(AGENT_CTX *ATX) {
if (ATX->classification != DSR_NONE && ATX->operating_mode == DSM_CLASSIFY)
{
LOG(LOG_ERR, ERR_AGENT_CLASSIFY_CLASS);
return EINVAL;
}
if (ATX->classification != DSR_NONE && ATX->source == DSS_NONE &&
!(ATX->flags & DAF_UNLEARN))
{
LOG(LOG_ERR, ERR_AGENT_NO_SOURCE);
return EINVAL;
}
if (ATX->source != DSS_NONE && ATX->classification == DSR_NONE)
{
LOG(LOG_ERR, ERR_AGENT_NO_CLASS);
return EINVAL;
}
if (ATX->operating_mode == DSM_NONE)
{
LOG(LOG_ERR, ERR_AGENT_NO_OP_MODE);
return EINVAL;
}
if (!_ds_match_attribute(agent_config, "ParseToHeaders", "on")) {
if (ATX->users->items == 0)
{
LOG(LOG_ERR, ERR_AGENT_USER_UNDEFINED);
return EINVAL;
}
}
return 0;
}
/*
* read_stdin(AGENT_CTX *)
*
* DESCRIPTION
* read message from stdin and perform any inline configuration
* (such as servicing 'ParseToHeaders' functions)
*
* INPUT ARGUMENTS
* ATX agent context
*
* RETURN VALUES
* buffer structure containing the message
*/
buffer * read_stdin(AGENT_CTX *ATX) {
int body = 0, line = 1;
char buf[1024];
buffer *msg;
msg = buffer_create(NULL);
if (msg == NULL) {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
return NULL;
}
if (_ds_match_attribute(agent_config, "DataSource", "document")) {
buffer_cat(msg, ": \n\n");
body = 1;
}
/* Only read the message if no signature was provided on commandline */
if (ATX->signature[0] == 0) {
while ((fgets (buf, sizeof (buf), stdin)) != NULL)
{
/* Strip CR/LFs for admittedly broken mail servers */
if (_ds_match_attribute(agent_config, "Broken", "lineStripping")) {
size_t len = strlen(buf);
while (len>1 && buf[len-2]==13) {
buf[len-2] = buf[len-1];
buf[len-1] = 0;
len--;
}
}
/*
* Don't include first line of message if it's a quarantine header added
* by dspam at time of quarantine
*/
if (line==1 && !strncmp(buf, "From QUARANTINE", 15))
continue;
/*
* Parse the "To" headers and adjust the operating mode and user when
* an email is sent to spam-* or notspam-* address. Behavior must be
* configured in dspam.conf
*/
if (_ds_match_attribute(agent_config, "ParseToHeaders", "on")) {
if (buf[0] == 0)
body = 1;
if (!body && !strncasecmp(buf, "To: ", 4))
process_parseto(ATX, buf);
}
if (buffer_cat (msg, buf))
{
LOG (LOG_CRIT, ERR_MEM_ALLOC);
goto bail;
}
/*
* Use the original user id if we are reversing a false positive
* (this is only necessary when using shared,managed groups
*/
if (!strncasecmp (buf, "X-DSPAM-User: ", 14) &&
ATX->operating_mode == DSM_PROCESS &&
ATX->classification == DSR_ISINNOCENT &&
ATX->source == DSS_ERROR)
{
char user[MAX_USERNAME_LENGTH];
strlcpy (user, buf + 14, sizeof (user));
chomp (user);
nt_destroy (ATX->users);
ATX->users = nt_create (NT_CHAR);
if (ATX->users == NULL) {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
goto bail;
}
LOGDEBUG("found username %s in X-DSPAM-User header", user);
nt_add (ATX->users, user);
}
line++;
}
}
if (!msg->used)
{
if (ATX->signature[0] != 0) {
buffer_cat(msg, "\n\n");
}
else {
LOG (LOG_INFO, "empty message (no data received)");
goto bail;
}
}
return msg;
bail:
LOGDEBUG("read_stdin() failure");
buffer_destroy(msg);
return NULL;
}
/*
* process_parseto(AGENT_CTX *, const char *)
*
* DESCRIPTION
* processes the To: line of a message to provide parseto services
*
* INPUT ARGUMENTS
* ATX agent context
* buf To: line
*
* RETURN VALUES
* returns 0 on success
*/
int process_parseto(AGENT_CTX *ATX, const char *buf) {
char *y = NULL;
char *x;
char *h = NULL;
char *buffer;
char *ptrptr;
if (!buf || strncmp(buf+2,":",1) != 0)
return EINVAL;
buffer = strdup (buf+3);
h = strtok_r (buffer, "\n", &ptrptr);
while (h != NULL) {
/* check for spam alias */
x = strstr(h, "classification = DSR_ISSPAM;
ATX->source = DSS_ERROR;
}
} else {
/* check for nonspam alias */
x = strstr(h, "= 9) {
y = strdup(x+9);
if (_ds_match_attribute(agent_config, "ChangeModeOnParse", "on")) {
ATX->classification = DSR_ISINNOCENT;
ATX->source = DSS_ERROR;
}
}
}
/* do not continue if we found a spam/nonspam alias */
if (y) break;
/* get next line from 'To' header */
h = strtok_r (NULL, "\n", &ptrptr);
if (h && h[0] != 32 && h[0] != 9) {
/* we are not any more in the 'To' header */
break;
}
}
free (buffer);
if (y && (_ds_match_attribute(agent_config,
"ChangeUserOnParse", "on") ||
_ds_match_attribute(agent_config,
"ChangeUserOnParse", "full") ||
_ds_match_attribute(agent_config,
"ChangeUserOnParse", "user")))
{
char *z;
if (_ds_match_attribute(agent_config,
"ChangeUserOnParse", "full"))
{
z = strtok_r(y, ">, \t\r\n", &ptrptr);
} else {
if (strstr(x, "@"))
z = strtok_r(y, "@", &ptrptr);
else
z = NULL;
}
if (z) {
nt_destroy(ATX->users);
ATX->users = nt_create(NT_CHAR);
if (!ATX->users) {
LOG(LOG_CRIT, ERR_MEM_ALLOC);
return EUNKNOWN;
}
nt_add (ATX->users, z);
}
}
if (y) free(y);
return 0;
}
int
init_pwent_cache(void)
{
struct passwd *pwent;
pwent = getpwuid(getuid());
if (pwent == NULL) {
return 0;
}
else {
__pw_name = strdup(pwent->pw_name);
__pw_uid = pwent->pw_uid;
}
return 1;
}