courier imap, authdaemond and vpopmails vchkpw

Posted by m6w6 on 14th March 2011 in Mike's sudden inspirations: WTF, SYS

A few years ago, it was possible to have courier-imap update the open-smtp relay file with it’s authvchkpw module. This feature and thus imap-before-smtp disappeared with the introduction of courier-authdaemond because the vchkpw code of authdaemond does not have a chance to see the TCPREMOTEIP environment variable.

I more or less lived with that, until a friend of mine got a new iphone… well yeah, one of those i-geeks, and that gadget apparently only supports imap, no pop.

To re-enable imap-before-smtp I wrote a little setuid wrapper, calling vpopmails open_smtp_relay(). This wrapper has to replace the imapd command in your courier-imap startup script and will exec imapd, after opening the smtp relay, wile imap-login is still in place to authenticate via authdaemond’s vchkpw module.

#include <unistd.h>  
#include <stdio.h>  
#include <sys/types.h>  
#include <vpopmail_config.h>  
#include <vauth.h>  
#include <vpopmail.h>  
  
#ifndef IMAPD  
#   define IMAPD "/usr/bin/imapd"  
#endif  
  
extern char **environ;  
  
int main(int argc, const char *argv[]) {  
    if (argc != 2) {  
        printf("1 NO argc != 2n");  
        return -1;  
    }  
  
    open_smtp_relay();  
    /* no need to chown vpopmail.vchkpw the open-smtp file  
     * because vchkpw used by qmail-pop3d is setuid root */  
  
    sleep(1);  
  
    if (setgid(VPOPMAILGID) || setuid(VPOPMAILUID)) {  
        printf("1 NO setuid/gid, getuid=%dn", getuid());  
        return -2;  
    }  
  
    execl(IMAPD, IMAPD, argv[1], NULL);  
  
    printf("1 NO exec %s failedn", IMAPD);  
    return -3;  
}

So while this worked to my satisfaction, I noticed that the tcp.smtp.cdb not always contained the IPs of the open-smtp file:

$ for ip in $(sed -re 's/:.*//' < open-smtp); do   
    cdbget $ip < tcp.smtp.cdb > /dev/null   
        || echo "$ip is missing";   
    done

I also found out that, while the vpopmail FAQ is claiming that clearopensmtp is requesting locks on the open-smtp(.lock) file, the source does not read like it would.

I opened a bug report for that issue, but as you might have already guessed, I’m running all of this on an archaic debian box, where no upgrades are planned. So I came up with another home-brewed solution:

#include <unistd.h>  
#include <stdio.h>  
#include <sys/types.h>  
#include <vpopmail_config.h>  
#include <vauth.h>  
#include <vpopmail.h>  
#include <fcntl.h>  
  
extern int get_write_lock(FILE*);  
extern int lock_reg(int,int,int,off_t,int,off_t);  
#define unlock(F) lock_reg(fileno(F), F_SETLK, F_UNLCK, 0, SEEK_SET, 0)  
  
int main(int argc, const char *argv[]) {  
    FILE *fs_temp, *fs_smtp, *fs_lock = fopen(OPEN_SMTP_LOK_FILE, "w+");  
    time_t clear = RELAY_CLEAR_MINUTES * 60, now = time(NULL);  
    int cc = 0, rc = 0;  
  
    if (!fs_lock) {  
        return -1;  
    }  
    if (get_write_lock(fs_lock)) {  
        unlock(fs_lock);  
        fclose(fs_lock);  
        return -2;  
    }     
  
    if (!(fs_temp = fopen(OPEN_SMTP_TMP_FILE ".clear", "w"))) {  
        rc = -3;  
    } else if (!(fs_smtp = fopen(OPEN_SMTP_CUR_FILE, "r+"))) {  
        fclose(fs_temp);  
        rc = -4;  
    } else {  
        while (!rc && !feof(fs_smtp)) {  
            unsigned stime;  
            char sdata[256];  
  
            switch (fscanf(fs_smtp, "%255st%un", sdata, &stime)) {  
                case 2:  
                    if ((clear + stime) >= now) {  
                        fprintf(fs_temp, "%st%un", sdata, stime);  
                    } else {  
                        ++cc;  
                    }  
                    break;  
  
                default:  
                    rc = -5;  
                case EOF:  
                    break;  
            }  
        }  
        fclose(fs_smtp);  
        fclose(fs_temp);  
    }  
  
    if (!rc) {  
        if (cc) {  
            if (rename(OPEN_SMTP_TMP_FILE ".clear", OPEN_SMTP_CUR_FILE)) {  
                rc = -6;  
            } else if(update_rules()) {  
                rc = -7;  
            }  
        } else {  
            unlink(OPEN_SMTP_TMP_FILE ".clear");  
        }  
    }  
  
    unlock(fs_lock);  
    fclose(fs_lock);  
    return rc;
}

It’s not the prettiest piece of code, but it helps.
Well, maybe someone else running last-millenium software finds this useful