chkusr 1.0 commented
This version of chkusr has been substituted by chkuser 2.0, that you find at this page.
qmail-smtpd-chkusr V. 1.0
>
> /*
> *
> * Patch 'qmail-smtpd-chkusr' v.1.0
> * for qmail 1.03 and vpopmail 5.3.3
> *
> * Antonio Nati tonix@interazioni.it
> *
> */
>
include files needed for patch to work
> #include <pwd.h>
> #include <sys/types.h>
> #include <unistd.h>
> #include <dirent.h>
> #include <stdio.h>
>
Change the following lines to point at your real directories.
open.h is in the qmail source directory and should remains as is.
vauth.h, vpopmail.h and vpopmail_config.h should be
in the vpopmail include dir.
>
> #include "open.h"
> #include "/vpopmail/include/vpopmail.h"
> #include "/vpopmail/include/vauth.h"
> #include "/vpopmail/include/vpopmail_config.h"
>
Routine emetting chkusr error
>
> void err_realrcpt() { out("550 sorry, no mailbox here by that name (#5.1.1 - chkusr)\r\n"); }
>
The complete routine handling all the chkusr stuff
>
> int realrcpt_check()
> {
> stralloc user = {0};
> stralloc domain = {0};
> stralloc domain_path = {0};
> stralloc bounce_path = {0};
> stralloc alias_name = {0};
> stralloc alias_path = {0};
> stralloc mailing_path = {0};
> int count;
> int retstat = 0;
> struct vqpasswd *user_passwd = NULL;
> int fd_file = -1;
> int read_char;
> DIR *dir_file = NULL;
> uid_t eff_uid;
> gid_t eff_gid;
> int offset;
> char read_buf[1024];
>
> /* if not local rcpthost we cannot control mailbox */
>
> if (!addrallowed()) { return 1; }
>
> /* Set up our variables */
>
Let's save the effective UID & GID, in order to use them later
to set back the original effective UID & GID
> /* qmail-smtpd is running now as (effective) qmaild:nofiles */
> /* Save the effective UID & GID (qmaild:nofiles) */
> eff_uid = geteuid ();
> eff_gid = getegid ();
>
> /* Search the '@' character */
> count = byte_rchr(addr.s,addr.len,'@');
>
> /* The following lines could interest people using # instead of @ in e-mail address */
> /* If '@' not found search the '%' character */
> /*
> if (count >= addr.len) {
> count = byte_rchr(addr.s,addr.len,'%');
> }
> */
>
Let's use Dan string routines to allocate the needed space on
variable modified by vpopmail or other calls external to qmail
> /*
> * Give extra room to variables used often or used outside stralloc_x calls
> * This should make all safer and even faster
> * (when these fields are used by stralloc_x routines)
> */
> if (!stralloc_ready (&domain, 200)) die_nomem();
> if (!stralloc_ready (&domain_path, 200)) die_nomem();
>
> if (count < addr.len) {
> if (!stralloc_copyb (&user, addr.s, count)) die_nomem();
> if (!stralloc_0 (&user)) die_nomem();
> if (!stralloc_copys (&domain, addr.s + count + 1)) die_nomem();
> if (!stralloc_0 (&domain)) die_nomem();
> }
> else {
> if (!stralloc_copys (&user, addr.s)) die_nomem();
> if (!stralloc_0 (&user)) die_nomem();
> if (!stralloc_copys (&domain, DEFAULT_DOMAIN)) die_nomem();
> if (!stralloc_0 (&domain)) die_nomem();
> }
>
> /* My personal control: continue only if a domain (default or not) is specified */
>
> if (domain.len == 1)
> return 0;
>
Let's set the new effective UID & GID.
Note carefully that now we cannot exit from the routine
until we've switched back to original UID & GID.
> /* Now set new effective UID & GID, getting it from real UID & GID (vpopmail:vchkpw) */
>
> setegid (getgid());
> seteuid (getuid());
>
> /* qmail-smtpd is running now as effective vpopmail:vchkpw */
>
> case_lowers (user.s);
> case_lowers (domain.s);
>
If domain is an alias domain, let's get the real domain
> /* Check if domain is a real domain */
>
> if (!stralloc_0 (&domain)) die_nomem();
> vget_real_domain(domain.s, domain.a);
>
> domain.len = strlen (domain.s);
> if (domain.len > (domain.a - 1)) die_nomem();
>
Let's get the domain's path, using vpopmail call vget_assign.
> /* Let's get domain's real path */
> vget_assign(domain.s, domain_path.s, 156, NULL, NULL);
>
> domain_path.len = strlen (domain_path.s);
>
Now the switch cascading suite of tests.
I've preferred this way of cascade checks, because is much
more readable and manageable than nested if
(too much if to be used!)
> /* Now Let's start the test suite */
>
> switch (0) {
>
Check if domain has bouncing enabled
> case 0:
> /* Check if domain has bouncing enabled */
>
> /* Allocate room for bounce_path */
> if (!stralloc_ready (&bounce_path, 200)) die_nomem();
> if (!stralloc_copy (&bounce_path, &domain_path)) die_nomem();
> if (!stralloc_cats (&bounce_path, "/.qmail-default")) die_nomem();
> if (!stralloc_0 (&bounce_path)) die_nomem();
>
> read_char = 0;
> fd_file = open_read (bounce_path.s);
> if (fd_file != -1) {
> read_char = read (fd_file, read_buf, sizeof(read_buf) - 1);
> close (fd_file);
> if (read_char < 0) read_char = 0;
> }
> read_buf[read_char] = 0;
>
> if ( strstr(read_buf, "bounce-no-mailbox") == NULL ) {
> retstat = 1;
> break;
> }
>
Check if vpopmail user exists
> case 1:
> /* User control: check the existence of a real user */
>
> user_passwd = vauth_getpw (user.s, domain.s);
> if (user_passwd != NULL) {
>
> /* If user exists check if he has BOUNCE_MAIL flag set */
>
> if (user_passwd->pw_gid & BOUNCE_MAIL)
> retstat = 0;
> else
> retstat = 1;
> break;
> }
>
Check aliases in the valias format
> case 2:
> /* Check for aliases/forwards - valias*/
>
> if (valias_select (user.s, domain.s) != NULL) {
> retstat = 1;
> break;
> }
>
Check aliases/forwards/robots in the .qmail-xxxx format
> case 3:
> /* Check for aliases/forwards - .qmail-x files */
>
> /* Allocate room for alias_path */
> if (!stralloc_ready (&alias_path, 200)) die_nomem();
> if (!stralloc_copy (&alias_name, &user)) die_nomem();
>
> /* Change all '.' in ':' before continuing on aliases */
> for (count = 0; count < alias_name.len; ++count)
> if (*(alias_name.s + count) == '.') *(alias_name.s + count) = ':';
>
> if (!stralloc_copy (&alias_path, &domain_path)) die_nomem();
> if (!stralloc_cats (&alias_path, "/.qmail-")) die_nomem();
> if (!stralloc_cats (&alias_path, alias_name.s)) die_nomem();
> if (!stralloc_0 (&alias_path)) die_nomem();
>
> /* access executes anyway as real (vpopmail:vchkpw), that's ok */
> if (access (alias_path.s, F_OK) == 0) {
> retstat = 1;
> break;
> }
>
Check for mailing list existence
> case 4:
> /* Let's check for mailing lists */
>
> /* Allocate room for mailing_path */
> if (!stralloc_ready (&mailing_path, 300)) die_nomem();
>
> /* Search for the outer '-' character */
> for (offset = user.len - 1; offset > 0; --offset)
> if (*(user.s + offset) == '-') {
> if (!stralloc_copy (&mailing_path, &domain_path)) die_nomem();
> if (!stralloc_cats (&mailing_path, "/")) die_nomem();
> if (!stralloc_catb (&mailing_path, user.s, offset)) die_nomem();
> if (!stralloc_cats (&mailing_path, "/mailinglist")) die_nomem();
> if (!stralloc_0 (&mailing_path)) die_nomem();
> /* access executes anyway as real (vpopmail:vchkpw), that's ok */
> if (access (mailing_path.s, F_OK) == 0) {
> retstat = 1;
> break;
> }
> }
>
The following code is necessary, in order to break the switch sequence,
only if someone adds other cases below.
> /*
> * Add this code if another case is following
> if (retstat == 1)
> break;
> */
>
> } /* end switch */
>
Resume original UID and GID
> /* Now switch back effective to saved UID & GID (qmaild:nofiles) */
>
> setegid (eff_gid);
> seteuid (eff_uid);
>
> /* qmail-smtpd is running again as (effective) qmaild:nofiles */
>
Exits the routine returning the result
> return retstat;
> }
>
Calling the checking user routine, within the existing
addrallowed function.
int addrallowed()
{
.........................
.........................
else
if (!addrallowed()) { err_nogateway(); return; }
+ if (!realrcpt_check()) { err_realrcpt(); return; }
if (!stralloc_cats(&rcptto,"T")) die_nomem();
if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
if (!stralloc_0(&rcptto)) die_nomem();
Document info
Page created on 5 march 2002, last modified on 5 march 2002.