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.