/* #specification: ldapgetcmd / principles This program listens (using inetd) on a TCP port. When it gets a connection, it sends a challenge and the client computes an SHA digest using this challenge and its private key, then sends its clientid and this digest followed by a one or more command. The digest is used to prove the identity of the sender. The client sends an ascii line and this is used to execute a command. Some sanity check is done. */ #include #include #include #include #include #include #include #include #include #include #include #include "ldapconf.h" #include #include #include #include #include "ldapconf.m" static int handle; static void fire_logerr(const char *s, ...) { char buf[1000]; va_list list; va_start (list,s); vsnprintf (buf,sizeof(buf)-1,s,list); va_end (list); struct sockaddr_in name; socklen_t len=sizeof(name); char adrstr[20]; strcpy (adrstr,"[unknown]"); if (getpeername(handle,(struct sockaddr*)&name,&len)!=-1){ unsigned long addr = ntohl(name.sin_addr.s_addr); ipnum_ip2a (addr,adrstr); } syslog (LOG_ERR,"From client %s, %s",adrstr,buf); } /* Create the challenge used by firelisten to encrypt its secret Return -1 if we can't create the challenge (resources ?) */ static int fire_formatkey (char challenge[30]) { int ret = -1; // We create the challenge by combining the current time // and a random number. This create a unique solution // We are keeping the file opened for all the session static int fd = -1; if (fd == -1){ fd = open ("/dev/urandom",O_RDONLY); } if (fd == -1){ syslog (LOG_ERR,"Can't open /dev/urandom (%s)",strerror(errno)); }else{ char tmp[8]; if (read (fd,tmp,8)!=8){ syslog (LOG_ERR,"Can't read 8 bytes from /dev/urandom"); }else{ for (int i=0; i<8; i++){ sprintf (challenge+i*2,"%02x",tmp[i]); } time_t ti = time(NULL); sprintf (challenge+16,"%lu",ti); ret = 0; } } return ret; } /* Check the secret of the provider Return != -1 if the secret was valid */ static int fire_auth(const char *challenge, const char *buf) { int ret = -1; SSTRING client,digest; const char *pt = str_copyword (client,buf); str_copyword (digest,pt); if (digest.is_empty()){ fire_logerr ("Invalid authentication"); }else{ FILE *fin = fopen ("/etc/ldapconf/suppliers.conf","r"); if (fin == NULL){ fire_logerr ("No /etc/ldapconf/suppliers.conf file"); }else{ char line[100]; bool client_found = false; while (fgets(line,sizeof(line)-1,fin)!=NULL){ if (line[0] == '#') continue; char *pt = strchr(line,':'); if (pt == NULL){ fire_logerr ("Invalid line in suppliers.conf: %s",line); break; }else{ *pt++ = '\0'; if (client.cmp(line)==0){ client_found = true; strip_end(pt); SSTRING tmp; tmp.setfromf ("%s%s",challenge,pt); misc_sha (tmp.get(),tmp); if (tmp.cmp(digest)!=0){ fire_logerr ("Invalid challenge response from supplier %s",client.get()); }else{ // Ok, the supplier is authentified // we accept its requests ret = 0; } break; } } } fclose (fin); if (!client_found){ fire_logerr ("Unknown supplier %s",client.get()); } } } return ret; } /* Execute a command. */ static void fire_process ( SSTRINGS &lines, POPENHANDLER *pop, const SSTRING &command) { for (int i=0; istrip_end (); const char *buf = line->get(); // Check for special shell characters const char *ivld = "\"'`\\/()#*?$&|"; while (*ivld != '\0'){ if (strchr(buf,*ivld)!=NULL){ fire_logerr ("Invalid character in request: %s",buf); break; } ivld++; } if (*ivld != '\0'){ lines.remove_del(0); i--; }else{ SSTRING cmd; cmd.setfromf ("%s %s",command.get(),buf); pop->add (new POPEN (cmd.get())); lines.remove_del(0); break; } } } int main (int argc, char *argv[]) { glocal SSTRING command; glocal const char *port = NULL; openlog ("ldapgetcmd",LOG_PID,LOG_AUTH); int ret = (argc,argv,"ldapconf"); extern const char *module_version; setproginfo ("ldapgetcmd",module_version,""); setarg ('c',"command",MSG_U(I_COMMAND,"Activation command"),glocal.command,true); setarg ('p',"port",MSG_U(I_LISTENPORT,"Listen on TCP port"),glocal.port,true); glocal POPENHANDLER *pop; glocal TCPSERVER *serv; glocal SSTRINGS lines; // Control command execution (); // Ok, a command has ended, is there another one fire_process (glocal.lines,glocal.pop,glocal.command); glocal.pop = &pop; // Manage several clients (glocal.port,100000000); set_timeout (20); handle = no; char challenge[30]; // syslog (LOG_ERR,"endclient0 %d %d",endclient,no); if (fire_formatkey(challenge)==-1){ endclient = true; }else{ sendf ("%s\n",challenge); // Record the challenge for reuse // syslog (LOG_ERR,"Avant record %d",endclient); info.data = new SSTRING(challenge); } // syslog (LOG_ERR,"Receive %d %d %s\n",no,state,line); handle = no; if (state == 0){ SSTRING *challenge = (SSTRING*)info.data; if (fire_auth(challenge->get(),line)==-1){ endclient = true; }else{ state = 1; } }else{ // Ok, the client is sending a command to execute glocal.lines.add (new SSTRING(line)); if (glocal.pop->getrunning()==0){ fire_process (glocal.lines,glocal.pop,glocal.command); } } // Synchronise the objects glocal.serv = &serv; (); if (strcmp(glocal.port,"__inetd__")==0 && glocal.pop->getrunning() == 0 && glocal.serv->getnbclients() == 0){ // syslog (LOG_ERR,"netevent end"); end = true; } mng.add (pop); mng.add (serv); mng.loop (20); return 0; // Unfortunatly, xinetd does not handle quoted argument // so we assume the extra arguments are the command line int ret = -1; if (argc > 0){ if (glocal.command.is_filled()){ for (int i=0; i return ret; }