recipes

packages recipes for the <noname> package manager
Log | Files | Refs

checkpassword.c (9606B)


      1 /* This is a checkpassword using LDAP for use with the qmail pop3d.
      2  * It can also be used as authentication module for courier imapd.
      3  * It assumes that all users have the same UID and GID and it is already
      4  * running as this user.  It will contact the LDAP server using
      5  * the given username and password as simple authentication credentials.
      6  * If the bind works, it will take the "homeDirectory" field from LDAP
      7  * and use that as home directory.  If the field is not there, it will
      8  * try to construct the home directory like this: take the "uid" field
      9  * and append it to HOME as #defined below to construct the home
     10  * directory.  If SUBDIRS is #defined, uid "leitner" will yield
     11  * HOME/l/leitner instead of HOME/leitner.  HOME has to end with a slash
     12  * in any case. */
     13 #include <unistd.h>
     14 #include <stdio.h>
     15 #include <stdlib.h>
     16 #include <time.h>
     17 #include <string.h>
     18 #include <signal.h>
     19 #include <errno.h>
     20 #include "byte.h"
     21 #include "buffer.h"
     22 #include "asn1.h"
     23 #include "ldap.h"
     24 #include "socket.h"
     25 #include "ip4.h"
     26 #include "str.h"
     27 #include "scan.h"
     28 
     29 #include <sys/types.h>
     30 #include <sys/wait.h>
     31 
     32 #include <fcntl.h>
     33 
     34 #define BUFSIZE 8192
     35 
     36 // #define HOME "/tmp/"
     37 #define HOME "/data/maildirs/"
     38 /* uncomment the following #define if you want mail for leitner to go to
     39  * HOME/l/leitner instead of HOME/leitner */
     40 #define SUBDIRS
     41 #define SHELL "/bin/sh"
     42 
     43 char* authenticated; /* getenv("AUTHENTICATED"); if this is not NULL, we
     44 			are running as courier authentication module and
     45 			not as checkpassword */
     46 static long messageid=1; /* LDAP message ID */
     47 
     48 static int ldapbind(int sock,char* user,char* passwd) {
     49   char outbuf[1024];
     50   int s=100;
     51   int len=fmt_ldapbindrequest(outbuf+s,3,user,passwd);
     52   int hlen=fmt_ldapmessage(0,messageid,BindRequest,len);
     53   int res;
     54   long op,Len,result;
     55   struct string matcheddn,errormessage,referral;
     56   fmt_ldapmessage(outbuf+s-hlen,messageid,BindRequest,len);
     57   if (write(sock,outbuf+s-hlen,len+hlen)!=len+hlen) return 0;
     58   len=read(sock,outbuf,1024);
     59   res=scan_ldapmessage(outbuf,outbuf+len,&messageid,&op,&Len);
     60   if (!res) return 0;
     61   if (op!=BindResponse) return 0;
     62   res=scan_ldapbindresponse(outbuf+res,outbuf+res+Len,&result,&matcheddn,&errormessage,&referral);
     63   if (!res) return 0;
     64   if (result) return 0;
     65   return 1;
     66 }
     67 
     68 static void maildirmake(const char* home) {
     69   int mask=umask(077);
     70   int fd=open(".",O_RDONLY);
     71   /* make sure the home and a maildir exist */
     72   if (chdir(home)==-1) {
     73     char* c=alloca(strlen(home)+1);
     74     char* d;
     75     /* not even the home exists! */
     76     strcpy(c,home);
     77     while ((d=strrchr(c,'/'))) {
     78       if (!d[1]) { *d=0; continue; }
     79       break;
     80     }
     81     *d=0; mkdir(c,0700);
     82     *d='/'; mkdir(c,0700);
     83     chdir(home);
     84   }
     85   if (mkdir("Maildir",0700)!=-1 || errno!=EEXIST) {
     86     chdir("Maildir");
     87     mkdir("tmp",0700);
     88     mkdir("new",0700);
     89     mkdir("cur",0700);
     90   }
     91   umask(mask);
     92   fchdir(fd);
     93   close(fd);
     94 }
     95 
     96 static struct string uid;
     97 
     98 static void doit(const char* login, const char** argv, char* homedir) {
     99   int len;
    100   char **env,**ep;
    101   char buf[100];
    102   char* home;
    103   char* mylogin;
    104 
    105   if (uid.l) { /* must have non-zero uid as we queried by uid */
    106     mylogin=alloca(uid.l+1);
    107     memmove(mylogin,uid.s,uid.l);
    108     mylogin[uid.l]=0;
    109     login=mylogin;
    110   }
    111   if (homedir)
    112     home=homedir;
    113   else {
    114     home=malloc(strlen(HOME)+strlen(login)+10);
    115     strcpy(home,HOME);
    116 #ifdef SUBDIRS
    117     buf[0]=login[0]; buf[1]='/'; buf[2]=0;
    118     strcat(home,buf);
    119 #endif
    120     strcat(home,login);
    121   }
    122   for (len=0; environ[len]; ++len) ;
    123   env=alloca((len+8)*sizeof(char*));
    124   ep=env;
    125   for (len=0; environ[len]; ++len) {
    126     if (str_start(environ[len],"USER=")) continue;
    127     if (str_start(environ[len],"HOME=")) continue;
    128     if (str_start(environ[len],"SHELL=")) continue;
    129     if (str_start(environ[len],"UID=")) continue;
    130     if (str_start(environ[len],"AUTHENTICATED=")) continue;
    131     *ep=environ[len]; ++ep;
    132   }
    133   *ep=alloca(strlen(SHELL)+7); strcat(strcpy(*ep,"SHELL="),SHELL); ++ep;
    134   *ep=alloca(strlen(login)+6); strcat(strcpy(*ep,"USER="),login); ++ep;
    135   *ep=alloca(strlen(home)+7); strcat(strcpy(*ep,"HOME="),home); ++ep;
    136   strcpy(buf,"UID=");
    137   buf[4+fmt_ulong(buf+4,geteuid())]=0;
    138   *ep=buf; ++ep;
    139   if (authenticated) {
    140     *ep=alloca(strlen(login)+20);
    141     strcat(strcpy(*ep,"AUTHENTICATED="),login); ++ep;
    142 
    143     *ep="MAILDIR="; ++ep;
    144     *ep="MAILDIRQUOTA="; ++ep;
    145 
    146     *ep=alloca(strlen(login)+20);
    147     strcat(strcpy(*ep,"AUTHADDR="),login); ++ep;
    148 #if 0
    149     *ep=alloca(strlen(fullname)+20);
    150     strcat(strcpy(*ep,"AUTHFULLNAME="),fullname); ++ep;
    151 #endif
    152   }
    153   *ep=0;
    154 
    155   maildirmake(home);
    156   if (chdir(home)==-1) {
    157     if (authenticated) {
    158       buffer_puts(buffer_2,"checkpassword: chdir(");
    159       buffer_puts(buffer_2,home);
    160       buffer_putsflush(buffer_2,") failed!\n");
    161       execve(argv[0],(char* const*)argv,environ);
    162     }
    163     exit(111);
    164   }
    165   execve(argv[0],(char* const*)argv,env);
    166   exit(111);
    167 }
    168 
    169 int main(int argc,char* argv[]) {
    170   int sock;
    171   char buf[BUFSIZE];
    172   int len=0;
    173   char cbuf[512];
    174   char* login,* password,* comment,* last;
    175 
    176   if ((authenticated=getenv("AUTHENTICATED"))) {
    177     /* courier mode */
    178     if (authenticated[0]) {
    179       /* already authenticated. */
    180       execve(argv[1],argv+1,environ);
    181       exit(111);
    182     }
    183   }
    184   if (!argv[1]) return 2;
    185   for (len=0; len<512; ) {
    186     int tmp;
    187     tmp=read(3,buf+len,512-len);
    188     if (tmp==-1) return 111;
    189     if (tmp==0) break;
    190     len+=tmp;
    191   }
    192   close(3);
    193   buf[len]=0; last=buf+len;
    194 
    195   login=buf;
    196   if (authenticated) {
    197     if (!str_start(login,"imap\nlogin\n") &&
    198 	!str_start(login,"pop3\nlogin\n")) {
    199       buffer_puts(buffer_2,"checkpassword: invalid check method: ");
    200       buffer_puts(buffer_2,login);
    201       buffer_putsflush(buffer_2,".\n");
    202       exit(111);
    203     }
    204     login+=11;
    205     password=strchr(login,'\n');
    206     if (!password) exit(111); *password=0; ++password;
    207     comment=strchr(password,'\n');
    208     if (!comment) exit(111); *comment=0; ++comment;
    209   } else {
    210     password=login+strlen(login)+1;
    211     comment=password+strlen(password)+1;
    212   }
    213   if (comment>last || (comment+strlen(comment)>last)) comment="";
    214 
    215   sock=socket_tcp4();
    216   {
    217     char ip[4];
    218     char *server=getenv("LDAPIP");
    219     if (!server) server="127.0.0.1";
    220     if (server[scan_ip4(server,ip)]) {
    221       buffer_putsflush(buffer_2,"invalid $LDAPIP\n");
    222       goto fail;
    223     }
    224     if (socket_connect4(sock,ip,389)) {
    225       buffer_putsflush(buffer_2,"could not connect to ldap server!\n");
    226       goto fail;
    227     }
    228   }
    229   if (ldapbind(sock,"","")) {
    230     char* founddn=0,*homedir=0;
    231     char* basedn;
    232     struct Filter f;
    233     struct AttributeDescriptionList adl,adl2;
    234     struct SearchRequest sr;
    235     int i;
    236     f.x=f.next=0;
    237     f.type=EQUAL;
    238     f.ava.desc.s="uid"; f.ava.desc.l=3;
    239     f.ava.value.s=login; f.ava.value.l=strlen(f.ava.value.s);
    240     f.a=0;
    241     adl.a.s="homeDirectory"; adl.a.l=strlen(adl.a.s);
    242     adl.next=&adl2;
    243     adl2.a.s="uid"; adl2.a.l=strlen(adl2.a.s);
    244     adl2.next=0;
    245     sr.baseObject.s=getenv("BASEOBJECT");
    246     if (!sr.baseObject.s) {
    247       buffer_putsflush(buffer_2,"$BASEOBJECT not set!\n");
    248       goto fail;
    249     }
    250     sr.baseObject.l=strlen(sr.baseObject.s);
    251     sr.scope=wholeSubtree; sr.derefAliases=neverDerefAliases;
    252     sr.sizeLimit=sr.timeLimit=sr.typesOnly=0;
    253     sr.filter=&f;
    254     sr.attributes=&adl;
    255     len=fmt_ldapsearchrequest(buf+100,&sr);
    256     {
    257       int tmp=fmt_ldapmessage(0,++messageid,SearchRequest,len);
    258       fmt_ldapmessage(buf+100-tmp,messageid,SearchRequest,len);
    259       write(sock,buf+100-tmp,len+tmp);
    260     }
    261     {
    262       char buf[8192];	/* arbitrary limit, bad! */
    263       int len=0,tmp,tmp2;
    264       char* max;
    265       struct SearchResultEntry sre;
    266       for (;;) {
    267 	long slen,mid,op;
    268 	len=0;
    269 	tmp=read(sock,buf+len,sizeof(buf)-len);
    270 	if (tmp<=0) {
    271 	  if (authenticated)
    272 	    buffer_putsflush(buffer_2,"checkpassword: error reading from socket to ldap server.\n");
    273 	  goto fail;
    274 	}
    275 	len+=tmp;
    276 nextmessage:
    277 	if ((tmp2=scan_ldapmessage(buf,buf+len,&mid,&op,&slen))) {
    278 	  max=buf+slen+tmp2;
    279 	  if (op==SearchResultEntry) {
    280 	    if ((tmp=scan_ldapsearchresultentry(buf+tmp2,max,&sre))) {
    281 	      if (!founddn) {
    282 		struct PartialAttributeList* pal=sre.attributes;
    283 		founddn=alloca(sre.objectName.l+1);
    284 		memcpy(founddn,sre.objectName.s,sre.objectName.l);
    285 		founddn[sre.objectName.l]=0;
    286 		while (pal) {
    287 		  struct AttributeDescriptionList* adl=pal->values;
    288 		  if (adl) {
    289 		    if (!matchstring(&pal->type,"homeDirectory")) {
    290 		      homedir=alloca(adl->a.l+1);
    291 		      memcpy(homedir,adl->a.s,adl->a.l);
    292 		      homedir[adl->a.l]=0;
    293 		    } else if (!matchstring(&pal->type,"uid"))
    294 		      uid=adl->a;
    295 		  }
    296 		  pal=pal->next;
    297 		}
    298 	      }
    299 	    } else {
    300 	      if (authenticated)
    301 		buffer_putsflush(buffer_2,"checkpassword: LDAP result parse error!\n");
    302 	      goto fail;
    303 	    }
    304 	  } else if (op==SearchResultDone) {
    305 	    if (!founddn) {
    306 	      if (authenticated)
    307 		buffer_putsflush(buffer_2,"checkpassword: no such uid in LDAP.\n");
    308 	      goto fail;
    309 	    }
    310 	    break;
    311 	  } else {
    312 	    if (authenticated)
    313 	      buffer_putsflush(buffer_2,"checkpassword: unexpected LDAP response.\n");
    314 	    goto fail;
    315 	  }
    316 	  if (max<buf+len) {
    317 	    byte_copy(buf,buf+len-max,max);
    318 	    tmp-=(max-(buf+len));
    319 	    len-=(max-(buf+len));
    320 	    goto nextmessage;
    321 	  }
    322 	}
    323       }
    324     }
    325     if (founddn) {
    326       if (ldapbind(sock,founddn,password)) {
    327 	close(sock);
    328 	doit(login,(const char**)argv+1,homedir);
    329 //	buffer_putsflush(buffer_1,"OK!\n");
    330 	return 0;
    331       } else
    332 	buffer_putsflush(buffer_1,"auth failure!\n");
    333     }
    334   } /* else authentication failed */
    335 fail:
    336   if (authenticated)
    337     execve(argv[1],argv+1,environ);
    338   return 111;
    339 }