1 /*
2  * Medusa Parallel Login Auditor
3  *
4  *    Copyright (C) 2006 Joe Mondloch
5  *    JoMo-Kun / jmk@foofus.net
6  *
7  *    This program is free software; you can redistribute it and/or modify
8  *    it under the terms of the GNU General Public License version 2,
9  *    as published by the Free Software Foundation
10  *
11  *    This program is distributed in the hope that it will be useful,
12  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *    GNU General Public License for more details.
15  *
16  *    http://www.gnu.org/licenses/gpl.txt
17  *
18  *    This program is released under the GPL with the additional exemption
19  *    that compiling, linking, and/or using OpenSSL is allowed.
20  *
21  * Based on ideas from Hydra 3.1 by VanHauser [vh@thc.org]
22  * Do only use for legal purposes. Illegal purposes cost $1 each.
23  *
24 */
25 
26 #define VERSION_SVN "$Id: medusa.c 9217 2015-05-07 18:07:03Z jmk $"
27 
28 #include <dlfcn.h>
29 #include "medusa.h"
30 #include "modsrc/module.h"
31 
32 char* szModuleName;
33 char* szTempModuleParam;
34 char* szModulePaths[3] = {"a", "b", "c"};         // will look at 3 different locations for modules if possible
35 char** arrModuleParams;    // the "argv" for the module
36 int nModuleParamCount;    // the "argc" for the module
37 //int ctrlc = 0;
38 sAudit *psAudit = NULL;
39 
freeModuleParams()40 void freeModuleParams()
41 {
42   int i;
43 
44   for (i = 0; i < nModuleParamCount; i++)
45   {
46     free(arrModuleParams[i]);
47   }
48 
49   free(arrModuleParams);
50 }
51 
52 /*
53   Display appropriate usage information for application.
54 */
usage()55 void usage()
56 {
57   writeVerbose(VB_NONE, "");
58   writeVerbose(VB_NONE, "Syntax: %s [-h host|-H file] [-u username|-U file] [-p password|-P file] [-C file] -M module [OPT]", PROGRAM);
59   writeVerbose(VB_NONE, "  -h [TEXT]    : Target hostname or IP address");
60   writeVerbose(VB_NONE, "  -H [FILE]    : File containing target hostnames or IP addresses");
61   writeVerbose(VB_NONE, "  -u [TEXT]    : Username to test");
62   writeVerbose(VB_NONE, "  -U [FILE]    : File containing usernames to test");
63   writeVerbose(VB_NONE, "  -p [TEXT]    : Password to test");
64   writeVerbose(VB_NONE, "  -P [FILE]    : File containing passwords to test");
65   writeVerbose(VB_NONE, "  -C [FILE]    : File containing combo entries. See README for more information.");
66   writeVerbose(VB_NONE, "  -O [FILE]    : File to append log information to");
67   writeVerbose(VB_NONE, "  -e [n/s/ns]  : Additional password checks ([n] No Password, [s] Password = Username)");
68   writeVerbose(VB_NONE, "  -M [TEXT]    : Name of the module to execute (without the .mod extension)");
69   writeVerbose(VB_NONE, "  -m [TEXT]    : Parameter to pass to the module. This can be passed multiple times with a");
70   writeVerbose(VB_NONE, "                 different parameter each time and they will all be sent to the module (i.e.");
71   writeVerbose(VB_NONE, "                 -m Param1 -m Param2, etc.)");
72   writeVerbose(VB_NONE, "  -d           : Dump all known modules");
73   writeVerbose(VB_NONE, "  -n [NUM]     : Use for non-default TCP port number");
74   writeVerbose(VB_NONE, "  -s           : Enable SSL");
75   writeVerbose(VB_NONE, "  -g [NUM]     : Give up after trying to connect for NUM seconds (default 3)");
76   writeVerbose(VB_NONE, "  -r [NUM]     : Sleep NUM seconds between retry attempts (default 3)");
77   writeVerbose(VB_NONE, "  -R [NUM]     : Attempt NUM retries before giving up. The total number of attempts will be NUM + 1.");
78   writeVerbose(VB_NONE, "  -c [NUM]     : Time to wait in usec to verify socket is available (default 500 usec).");
79   writeVerbose(VB_NONE, "  -t [NUM]     : Total number of logins to be tested concurrently");
80   writeVerbose(VB_NONE, "  -T [NUM]     : Total number of hosts to be tested concurrently");
81   writeVerbose(VB_NONE, "  -L           : Parallelize logins using one username per thread. The default is to process ");
82   writeVerbose(VB_NONE, "                 the entire username before proceeding.");
83   writeVerbose(VB_NONE, "  -f           : Stop scanning host after first valid username/password found.");
84   writeVerbose(VB_NONE, "  -F           : Stop audit after first valid username/password found on any host.");
85   writeVerbose(VB_NONE, "  -b           : Suppress startup banner");
86   writeVerbose(VB_NONE, "  -q           : Display module's usage information");
87   writeVerbose(VB_NONE, "  -v [NUM]     : Verbose level [0 - 6 (more)]");
88   writeVerbose(VB_NONE, "  -w [NUM]     : Error debug level [0 - 10 (more)]");
89   writeVerbose(VB_NONE, "  -V           : Display version");
90   writeVerbose(VB_NONE, "  -Z [TEXT]    : Resume scan based on map of previous scan");
91   writeVerbose(VB_NONE, "\n");
92   return;
93 }
94 
95 /*
96   Read user options and check validity.
97 */
checkOptions(int argc,char ** argv,sAudit * _psAudit)98 int checkOptions(int argc, char **argv, sAudit *_psAudit)
99 {
100   int opt;
101   extern char *optarg;
102   extern int   opterr;
103   int ret = 0;
104   int i = 0;
105   int nIgnoreBanner = 0;
106 
107   /* initialize options */
108   _psAudit->iServerCnt = 1;
109   _psAudit->iLoginCnt = 1;
110   _psAudit->iParallelLoginFlag = PARALLEL_LOGINS_PASSWORD;
111   _psAudit->iPortOverride = 0;                        /* Use default port */
112   _psAudit->iUseSSL = 0;                              /* No SSL */
113   _psAudit->iTimeout = DEFAULT_WAIT_TIME;             /* Default wait of 3 seconds */
114   _psAudit->iRetryWait = WAIT_BETWEEN_CONNECT_RETRY;  /* Default wait of 3 seconds */
115   _psAudit->iRetries = MAX_CONNECT_RETRY;             /* Default of 2 retries (3 total attempts) */
116   _psAudit->iSocketWait = 500;                        /* Default wait of 500 usec */
117   _psAudit->iShowModuleHelp = 0;
118   iVerboseLevel = 5;
119   iErrorLevel = 5;
120 
121   for (i =0; i < argc; i++)
122   {
123     if (strstr(argv[i], "-b") != NULL)
124     {
125       nIgnoreBanner = 1;
126       break;
127     }
128   }
129 
130   if (nIgnoreBanner == 0)
131     writeVerbose(VB_NONE, "%s v%s [%s] (C) %s %s\n", PROGRAM, VERSION, WWW, AUTHOR, EMAIL);
132 
133   while ((opt = getopt(argc, argv, "h:H:u:U:p:P:C:O:e:M:m:g:r:R:c:t:T:n:bqdsLfFVv:w:Z:")) != EOF)
134   {
135     switch (opt)
136     {
137     case 'h':
138       if (_psAudit->HostType)
139       {
140         writeError(ERR_ALERT, "Options 'h' and 'H' are mutually exclusive.");
141         ret = EXIT_FAILURE;
142       }
143       else
144       {
145         _psAudit->pGlobalHost = malloc( strlen(optarg) + 1 );
146         memset(_psAudit->pGlobalHost, 0, strlen(optarg) + 1);
147         strncpy(_psAudit->pGlobalHost, optarg, strlen(optarg));
148         _psAudit->HostType = L_SINGLE;
149       }
150       break;
151     case 'H':
152       if (_psAudit->HostType)
153       {
154         writeError(ERR_ALERT, "Options 'h' and 'H' are mutually exclusive.");
155         ret = EXIT_FAILURE;
156       }
157       else
158       {
159         _psAudit->pOptHost = malloc( strlen(optarg) + 1 );
160         memset(_psAudit->pOptHost, 0, strlen(optarg) + 1);
161         strncpy(_psAudit->pOptHost, optarg, strlen(optarg));
162         _psAudit->HostType = L_FILE;
163       }
164       break;
165     case 'u':
166       if (_psAudit->UserType)
167       {
168         writeError(ERR_ALERT, "Options 'u' and 'U' are mutually exclusive.");
169         ret = EXIT_FAILURE;
170       }
171       else
172       {
173         _psAudit->pGlobalUser = malloc( strlen(optarg) + 1 );
174         memset(_psAudit->pGlobalUser, 0, strlen(optarg) + 1);
175         strncpy(_psAudit->pGlobalUser, optarg, strlen(optarg));
176         _psAudit->UserType = L_SINGLE;
177         _psAudit->iUserCnt = 1;
178       }
179       break;
180     case 'U':
181       if (_psAudit->UserType)
182       {
183         writeError(ERR_ALERT, "Options 'u' and 'U' are mutually exclusive.");
184         ret = EXIT_FAILURE;
185       }
186       else
187       {
188         _psAudit->pOptUser = malloc( strlen(optarg) + 1 );
189         memset(_psAudit->pOptUser, 0, strlen(optarg) + 1);
190         strncpy(_psAudit->pOptUser, optarg, strlen(optarg));
191         _psAudit->UserType = L_FILE;
192       }
193       break;
194     case 'p':
195       if (_psAudit->PassType)
196       {
197         writeError(ERR_ALERT, "Options 'p' and 'P' are mutually exclusive.");
198         ret = EXIT_FAILURE;
199       }
200       else
201       {
202         _psAudit->pGlobalPass = malloc( strlen(optarg) + 2 );
203         memset(_psAudit->pGlobalPass, 0, strlen(optarg) + 2);
204         strncpy(_psAudit->pGlobalPass, optarg, strlen(optarg));
205         _psAudit->PassType = L_SINGLE;
206         _psAudit->iPassCnt = 1;
207       }
208       break;
209     case 'P':
210       if (_psAudit->PassType)
211       {
212         writeError(ERR_ALERT, "Options 'p' and 'P' are mutually exclusive.");
213         ret = EXIT_FAILURE;
214       }
215       else
216       {
217         _psAudit->pOptPass = malloc( strlen(optarg) + 1 );
218         memset(_psAudit->pOptPass, 0, strlen(optarg) + 1);
219         strncpy(_psAudit->pOptPass, optarg, strlen(optarg));
220         _psAudit->PassType = L_FILE;
221       }
222       break;
223     case 'C':
224       _psAudit->pOptCombo = malloc( strlen(optarg) + 1 );
225       memset(_psAudit->pOptCombo, 0, strlen(optarg) + 1);
226       strncpy(_psAudit->pOptCombo, optarg, strlen(optarg));
227       break;
228     case 'O':
229       _psAudit->pOptOutput = malloc( strlen(optarg) + 1 );
230       memset(_psAudit->pOptOutput, 0, strlen(optarg) + 1);
231       strncpy(_psAudit->pOptOutput, optarg, strlen(optarg));
232       break;
233     case 'e':
234       if (strcmp(optarg, "n") == 0)
235       {
236         _psAudit->iPasswordBlankFlag = TRUE;
237         _psAudit->iPasswordUsernameFlag = FALSE;
238       }
239       else if (strcmp(optarg, "s") == 0)
240       {
241         _psAudit->iPasswordBlankFlag = FALSE;
242         _psAudit->iPasswordUsernameFlag = TRUE;
243       }
244       else if ((strcmp(optarg, "ns") == 0) || (strcmp(optarg, "sn") == 0))
245       {
246         _psAudit->iPasswordBlankFlag = TRUE;
247         _psAudit->iPasswordUsernameFlag = TRUE;
248       }
249       else
250       {
251         writeError(ERR_ALERT, "Option 'e' requires value of n, s, or ns.");
252         ret = EXIT_FAILURE;
253       }
254       break;
255     case 's':
256       _psAudit->iUseSSL = 1;
257       break;
258     case 'L':
259       _psAudit->iParallelLoginFlag = PARALLEL_LOGINS_USER;
260       break;
261     case 'f':
262       _psAudit->iFoundPairExitFlag = FOUND_PAIR_EXIT_HOST;
263       break;
264     case 'F':
265       _psAudit->iFoundPairExitFlag = FOUND_PAIR_EXIT_AUDIT;
266       break;
267     case 't':
268       _psAudit->iLoginCnt = atoi(optarg);
269       break;
270     case 'T':
271       _psAudit->iServerCnt = atoi(optarg);
272       break;
273     case 'n':
274       _psAudit->iPortOverride = atoi(optarg);
275       break;
276     case 'v':
277       iVerboseLevel = atoi(optarg);
278       break;
279     case 'w':
280       iErrorLevel = atoi(optarg);
281       break;
282     case 'V':
283       writeVerbose(VB_EXIT, "");  // Terminate now
284       break;
285     case 'M':
286       szModuleName = malloc(strlen(optarg) + 1);
287       memset(szModuleName, 0, strlen(optarg) + 1);
288       strncpy(szModuleName, optarg, strlen(optarg));
289       _psAudit->pModuleName = szModuleName;
290       break;
291     case 'm':
292       nModuleParamCount++;
293       szTempModuleParam = malloc(strlen(optarg) + 1);
294       memset(szTempModuleParam, 0, strlen(optarg) + 1);
295       strncpy(szTempModuleParam, optarg, strlen(optarg));
296       arrModuleParams = realloc(arrModuleParams, nModuleParamCount * sizeof(char*));
297       arrModuleParams[nModuleParamCount - 1] = szTempModuleParam;
298       break;
299     case 'd':
300       listModules(szModulePaths, 1);  // End the program after this executes by passing a 1 as the second param
301       break;
302     case 'b':
303       // Do nothing - supression of the startup banner is handled before the switch statement
304       break;
305     case 'q':
306       _psAudit->iShowModuleHelp = 1;
307       break;
308     case 'g':
309       _psAudit->iTimeout = atoi(optarg);
310       break;
311     case 'r':
312       _psAudit->iRetryWait = atoi(optarg);
313       break;
314     case 'R':
315       _psAudit->iRetries = atoi(optarg);
316       break;
317     case 'c':
318       _psAudit->iSocketWait = atoi(optarg);
319       break;
320     case 'Z':
321       _psAudit->pOptResume = malloc( strlen(optarg) + 1 );
322       memset(_psAudit->pOptResume, 0, strlen(optarg) + 1);
323       strncpy(_psAudit->pOptResume, optarg, strlen(optarg));
324       break;
325     default:
326       writeError(ERR_CRITICAL, "Unknown error processing command-line options.");
327       ret = EXIT_FAILURE;
328     }
329   }
330 
331   if (argc <= 1) {
332     ret = EXIT_FAILURE;
333   }
334 
335   if (_psAudit->iShowModuleHelp)
336   {
337     ret = invokeModule(_psAudit->pModuleName, NULL, 0, NULL);
338     if (ret < 0)
339     {
340       writeError(ERR_CRITICAL, "invokeModule failed - see previous errors for an explanation");
341     }
342   }
343   else
344   {
345     if ( !((_psAudit->HostType) || (_psAudit->pOptCombo)) )
346     {
347       writeError(ERR_ALERT, "Host information must be supplied.");
348       ret = EXIT_FAILURE;
349     }
350     else if ( !((_psAudit->UserType) || (_psAudit->pOptCombo)) )
351     {
352       writeError(ERR_ALERT, "User logon information must be supplied.");
353       ret = EXIT_FAILURE;
354     }
355     else if ( !((_psAudit->PassType) || (_psAudit->pOptCombo) || (_psAudit->iPasswordBlankFlag) || ( _psAudit->iPasswordUsernameFlag)) )
356     {
357       writeError(ERR_ALERT, "Password information must be supplied.");
358       ret = EXIT_FAILURE;
359     }
360   }
361 
362   return ret;
363 }
364 
invokeModule(char * pModuleName,sLogin * pLogin,int argc,char * argv[])365 int invokeModule(char* pModuleName, sLogin* pLogin, int argc, char* argv[])
366 {
367   void    *pLibrary;
368   int    iReturn;
369   function_go  pGo;
370   function_showUsage pUsage;
371   char* modPath;
372   int nPathLength;
373   int i;
374   int nSuccess = 0;
375 
376   iReturn   = -1;
377   pLibrary  = NULL;
378   pGo       = NULL;
379   pUsage    = NULL;
380 
381   if (NULL == pModuleName)
382   {
383     listModules(szModulePaths, 0);
384     writeError(ERR_CRITICAL, "invokeModule called with no name");
385     return -1;
386   }
387 
388   // Find the first available path to use
389   for(i = 0; i < 3; i++)
390   {
391     if (szModulePaths[i] != NULL)
392     {
393       // Is the module available under here?
394       writeError(ERR_DEBUG, "Trying module path of %s", szModulePaths[i]);
395       nPathLength = strlen(szModulePaths[i]) + strlen(pModuleName) + strlen(MODULE_EXTENSION) + 2;  // Going to add a slash too
396       modPath = malloc(nPathLength);
397       memset(modPath, 0, nPathLength);
398       strncpy(modPath, szModulePaths[i], strlen(szModulePaths[i]));
399       strncat(modPath, "/", 1);
400       strncat(modPath, pModuleName, strlen(pModuleName));
401       strncat(modPath, MODULE_EXTENSION, strlen(MODULE_EXTENSION));
402 
403       // Now try the load
404       writeError(ERR_DEBUG, "Attempting to load %s", modPath);
405       pLibrary = dlopen(modPath, RTLD_NOW);
406 
407       if (pLibrary == NULL)
408       {
409         continue;
410       }
411       else if (!pLogin)
412       {
413         pUsage = (function_showUsage)dlsym(pLibrary, "showUsage");
414 
415         writeError(ERR_DEBUG, "Attempting to display usage information for module: %s", modPath);
416 
417         if (pUsage == NULL)
418         {
419           writeError(ERR_ALERT, "Couldn't get a pointer to \"showUsage\" for module %s [%s]", modPath, dlerror());
420           return -1;
421         }
422         else
423         {
424           nSuccess = 1;
425           pUsage();
426         }
427         dlclose(pLibrary);
428         exit(EXIT_SUCCESS); // TEMP FIX
429       }
430       else
431       {
432         pGo = (function_go)dlsym(pLibrary, "go");
433 
434         if (pGo == NULL)
435         {
436           writeError(ERR_ALERT, "Couldn't get a pointer to \"go\" for module %s [%s]", modPath, dlerror());
437           return -1;
438         }
439         else
440         {
441           nSuccess = 1;
442           iReturn = pGo(pLogin, argc, argv);
443           break;
444         }
445         dlclose(pLibrary);
446       }
447     }
448   }
449 
450   if (!nSuccess)
451   {
452     writeVerbose(VB_IMPORTANT, "Couldn't load \"%s\" [%s]. Place the module in the medusa directory, set the MEDUSA_MODULE_NAME environment variable or run the configure script again using --with-default-mod-path=[path].", pModuleName, dlerror());
453     iReturn = -1;
454   }
455 
456   return iReturn;
457 }
458 
459 /*
460   Read the contents of a user supplied file. Store contents in memory and provide
461   a count of the total file lines processed.
462 */
loadFile(char * pFile,char ** pFileContent,int * iFileCnt)463 void loadFile(char *pFile, char **pFileContent, int *iFileCnt)
464 {
465   FILE *pfFile;
466   size_t stFileSize = 0;
467   char tmp[MAX_BUF];
468   char *ptr;
469 
470   *iFileCnt = 0;
471 
472   if ((pfFile = fopen(pFile, "r")) == NULL)
473   {
474     writeError(ERR_FATAL, "Failed to open file %s - %s", pFile, strerror( errno ) );
475   }
476   else
477   {
478     /* get file stats */
479     while (! feof(pfFile) )
480     {
481       if ( fgets(tmp, MAX_BUF, pfFile) != NULL )
482       {
483         if (tmp[0] != '\0')
484         {
485           stFileSize += strlen(tmp) + 1;
486           (*iFileCnt)++;
487         }
488       }
489     }
490     rewind(pfFile);
491 
492     *pFileContent = malloc(stFileSize + 1);    /* extra end NULL */
493 
494     if (pFileContent == NULL)
495     {
496       writeError(ERR_FATAL, "Failed to allocate memory for file %s.", pFile);
497     }
498 
499     memset(*pFileContent, 0, stFileSize + 1);
500     ptr = *pFileContent;
501 
502     /* load file into mem */
503     while (! feof(pfFile) )
504     {
505       if (fgets(tmp, MAX_BUF, pfFile) != NULL)
506       {
507         /* ignore blank lines */
508         if ((tmp[0] == '\n') || (tmp[0] == '\r'))
509         {
510           (*iFileCnt)--;
511           writeError(ERR_DEBUG, "Ignoring blank line in file: %s. Resetting total count: %d.", pFile, (*iFileCnt));
512         }
513         else if (tmp[0] != '\0')
514         {
515           if (tmp[strlen(tmp) - 1] == '\n') tmp[strlen(tmp) - 1] = '\0';
516           if (tmp[strlen(tmp) - 1] == '\r') tmp[strlen(tmp) - 1] = '\0';
517           memcpy(ptr, tmp, strlen(tmp) + 1);
518           ptr += strlen(tmp) + 1;
519         }
520       }
521     }
522     *ptr = '\0';  /* extra NULL to identify end of list */
523   }
524 
525   if((*iFileCnt) == 0)
526   {
527     writeError(ERR_FATAL, "Error loading user supplied file (%s) -- file may be empty.", pFile);
528   }
529 
530   free(pFile);
531   return;
532 }
533 
534 /*
535   Examine the first row of the combo file to determine information provided.
536   Combo files are colon separated and in the following format: host:user:password.
537   If any of the three fields are left empty, the respective information should be
538   provided either as a single global value or as a list in a file.
539 
540   The following combinations are possible in the combo file:
541   1. foo:bar:fud
542   2. foo:bar:
543   3. foo::
544   4. :bar:fud
545   5. :bar:
546   6. ::fud
547   7. foo::fud
548 
549   Medusa also supports using PwDump files as a combo file. The format of these
550   files should be user:id:lm:ntlm. We look for ':::' at the end of the first line
551   to determine if the file contains PwDump output. In addition, a LM/NTLM hash
552   pair can be supplied in lieu of a password (e.g. host:user:lm:ntlm).
553 */
processComboFile(sAudit ** _psAudit)554 int processComboFile(sAudit **_psAudit)
555 {
556   int ret = 0, iColonCount = 0;
557   char *pComboTmp;
558 
559   writeError(ERR_DEBUG, "[processComboFile] Processing user supplied combo file.");
560 
561   pComboTmp = (*_psAudit)->pGlobalCombo;
562 
563   /* PwDump file check */
564   /* USERNAME:ID:LM HASH:NTLM HASH::: */
565   writeError(ERR_DEBUG, "[processComboFile] PwDump file check.");
566   while (*pComboTmp != '\0')
567   {
568     if (strcmp(pComboTmp, ":::") == 0)
569     {
570       iColonCount += 3;
571       pComboTmp += 3;
572     }
573     else if (*pComboTmp == ':')
574     {
575       iColonCount++;
576       pComboTmp++;
577     }
578     else
579     {
580       pComboTmp++;
581     }
582 
583     if ((iColonCount == 6) && (*pComboTmp == '\0')) {
584       writeError(ERR_DEBUG, "[processComboFile] Combo format scan detected PwDump file.");
585 
586       if (((*_psAudit)->HostType != L_SINGLE) && ((*_psAudit)->HostType != L_FILE))
587       {
588         writeError(ERR_FATAL, "Combo format used requires host information via (-h/-H).");
589       }
590 
591       if (((*_psAudit)->UserType != L_SINGLE) && ((*_psAudit)->UserType != L_FILE))
592       {
593         (*_psAudit)->UserType = L_PWDUMP;
594       }
595 
596       (*_psAudit)->PassType = L_PWDUMP;
597 
598       return ret;
599     }
600   }
601 
602   if ( ! ((iColonCount == 2) || (iColonCount == 3)) )
603   {
604     writeError(ERR_DEBUG, "[processComboFile] Number of colons detected in first entry: %d", iColonCount);
605     writeError(ERR_FATAL, "Invalid combo file format.");
606   }
607 
608   pComboTmp = (*_psAudit)->pGlobalCombo;
609 
610   if (*pComboTmp == ':')
611   {               /* no host specified */
612     writeError(ERR_DEBUG, "[processComboFile] No host combo field specified.");
613     if (((*_psAudit)->HostType != L_SINGLE) && ((*_psAudit)->HostType != L_FILE))
614     {
615       writeError(ERR_FATAL, "Combo format used requires host information via (-h/-H).");
616     }
617   }
618   else
619   {
620     writeError(ERR_DEBUG, "[processComboFile] Host combo field specified.");
621     (*_psAudit)->HostType = L_COMBO;
622 
623     while (*pComboTmp != ':')
624     {
625       if (pComboTmp == NULL)
626       {
627         writeError(ERR_FATAL, "Failed to process combo file. Incorrect format.");
628       }
629 
630       pComboTmp++;
631     }
632   }
633   pComboTmp++;
634 
635   if (*pComboTmp == ':')
636   {              /* no user specified */
637     writeError(ERR_DEBUG, "[processComboFile] No user combo field specified.");
638     if (((*_psAudit)->UserType != L_SINGLE) && ((*_psAudit)->UserType != L_FILE))
639     {
640       writeError(ERR_FATAL, "Combo format used requires user information via (-u/-U).");
641     }
642   }
643   else
644   {
645     writeError(ERR_DEBUG, "[processComboFile] User combo field specified.");
646     (*_psAudit)->UserType = L_COMBO;
647 
648     while (*pComboTmp != ':')
649     {
650       if (pComboTmp == NULL)
651       {
652         writeError(ERR_FATAL, "Failed to process combo file. Incorrect format.");
653       }
654 
655       pComboTmp++;
656     }
657   }
658   pComboTmp++;
659 
660   if (*pComboTmp == '\0')
661   {             /* no password specified */
662     writeError(ERR_DEBUG, "[processComboFile] No password combo field specified.");
663     if (((*_psAudit)->PassType != L_SINGLE) && ((*_psAudit)->PassType != L_FILE) &&
664         ((*_psAudit)->iPasswordBlankFlag == FALSE) && ((*_psAudit)->iPasswordUsernameFlag == FALSE))
665     {
666       writeError(ERR_FATAL, "Combo format used requires password information via (-p/-P).");
667     }
668   }
669   else
670   {
671     writeError(ERR_DEBUG, "[processComboFile] Password combo field specified.");
672     (*_psAudit)->PassType = L_COMBO;
673   }
674 
675   return ret;
676 }
677 
678 
679 /*
680   Return next user-specified host during audit data table building process.
681   This host information may be a single global entry, from a file containing
682   a list of hosts, or from a combo file.
683 */
findNextHost(sAudit * _psAudit,char * _pHost)684 char* findNextHost(sAudit *_psAudit, char *_pHost)
685 {
686 
687   if (_psAudit->pGlobalCombo)
688   {
689     writeError(ERR_DEBUG, "[findNextHost] Process global combo file.");
690     /* advance to next entry in combo list */
691     if ((_psAudit->iUserListFlag == LIST_COMPLETE) && (_psAudit->iHostListFlag == LIST_COMPLETE))
692     {
693       writeError(ERR_DEBUG, "[findNextHost] Advance to next entry in combo list.");
694       /* skip host */
695       while (*_psAudit->pGlobalCombo != '\0')
696         _psAudit->pGlobalCombo++;
697       _psAudit->pGlobalCombo++;
698 
699       /* skip user */
700       while (*_psAudit->pGlobalCombo != '\0')
701         _psAudit->pGlobalCombo++;
702       _psAudit->pGlobalCombo++;
703 
704       /* skip pass */
705       while (*_psAudit->pGlobalCombo != '\0')
706         _psAudit->pGlobalCombo++;
707       _psAudit->pGlobalCombo++;
708 
709       if (*_psAudit->pGlobalCombo == '\0')
710       {
711         _psAudit->iAuditFlag = AUDIT_COMPLETE;
712       }
713       else
714       {
715         _psAudit->iAuditFlag = AUDIT_IN_PROGRESS;
716       }
717     }
718 
719     /* convert ':' to '\0' in combo entries */
720     if ((_psAudit->pComboEntryTmp == NULL) || ((_psAudit->iUserListFlag == LIST_COMPLETE) && (_psAudit->iHostListFlag == LIST_COMPLETE)))
721     {
722       writeError(ERR_DEBUG, "[findNextHost] Convert ':' to '\\0' in combo entries.");
723       _psAudit->pComboEntryTmp = _psAudit->pGlobalCombo;
724 
725       if (*_psAudit->pComboEntryTmp != '\0')
726       {
727         /* host:user ==> host\0user */
728         while (*_psAudit->pComboEntryTmp != ':')
729           _psAudit->pComboEntryTmp++;
730         memset(_psAudit->pComboEntryTmp, 0, 1);
731 
732         /* user:pass ==> user\0pass */
733         while (*_psAudit->pComboEntryTmp != ':')
734           _psAudit->pComboEntryTmp++;
735         memset(_psAudit->pComboEntryTmp, 0, 1);
736       }
737     }
738 
739     _psAudit->pComboEntryTmp = _psAudit->pGlobalCombo;
740   }
741   else
742   {
743     if ((_psAudit->iUserListFlag == LIST_COMPLETE) && (_psAudit->iHostListFlag == LIST_COMPLETE))
744     {
745       _psAudit->iAuditFlag = AUDIT_COMPLETE;
746     }
747   }
748 
749   _psAudit->iHostListFlag = LIST_COMPLETE;
750 
751   if (_psAudit->iAuditFlag == AUDIT_COMPLETE)
752   {
753     _pHost = NULL;
754   }
755   else if (_psAudit->HostType == L_COMBO)
756   {
757     if (*_psAudit->pGlobalCombo == '\0')
758     {
759       _pHost = NULL;
760     }
761     else
762     {
763       _pHost = _psAudit->pGlobalCombo;
764     }
765   }
766   else if (_psAudit->HostType == L_FILE)
767   {
768     if (*_psAudit->pGlobalHost != '\0')
769     {
770       _pHost = _psAudit->pGlobalHost;
771 
772       /* advancing host list */
773       while (*_psAudit->pGlobalHost != '\0')
774         _psAudit->pGlobalHost++;
775       _psAudit->pGlobalHost++;
776 
777       if (*_psAudit->pGlobalHost != '\0')
778       {
779         _psAudit->iHostListFlag = LIST_IN_PROGRESS;
780       }
781       else
782       {
783         /* resetting host list */
784         _psAudit->pGlobalHost = _psAudit->pHostFile;
785       }
786     }
787   }
788   else if (_psAudit->HostType == L_SINGLE)
789   {
790     _pHost = _psAudit->pGlobalHost;
791     _psAudit->iAuditFlag = AUDIT_COMPLETE;
792   }
793   else
794   {
795     writeError(ERR_FATAL, "[findNextHost] HostType not properly defined.");
796   }
797 
798   return _pHost;
799 }
800 
801 
802 /*
803   Return next user-specified user during audit data table building process.
804   This host information may be a single global entry, from a file containing
805   a list of users, or from a combo file.
806 */
findNextUser(sAudit * _psAudit,char * _pUser)807 char* findNextUser(sAudit *_psAudit, char *_pUser)
808 {
809   char* pComboTmp;
810 
811   _psAudit->iUserListFlag = LIST_COMPLETE;
812 
813   if (_psAudit->UserType == L_COMBO)
814   {
815     /* advance to username */
816     if (_psAudit->pGlobalCombo)
817     {
818       pComboTmp = _psAudit->pComboEntryTmp;
819       while (*pComboTmp != '\0')
820         pComboTmp++;
821       pComboTmp++;
822     }
823 
824     if (_pUser != NULL)
825       _pUser = NULL;
826     else
827       _pUser = pComboTmp;
828 
829     writeError(ERR_DEBUG, "[findNextUser] Combo User: %s", _pUser);
830   }
831   else if (_psAudit->UserType == L_PWDUMP)
832   {
833     if (_pUser != NULL)
834       _pUser = NULL;
835     else
836       _pUser = _psAudit->pComboEntryTmp;
837 
838     writeError(ERR_DEBUG, "[findNextUser] PwDump User: %s", _pUser);
839   }
840   else if (_psAudit->UserType == L_FILE)
841   {
842     _pUser = _psAudit->pGlobalUser;
843 
844     if (*_psAudit->pGlobalUser != '\0')
845     {
846       /* advance user list pointer */
847       while (*_psAudit->pGlobalUser != '\0')
848         _psAudit->pGlobalUser++;
849       _psAudit->pGlobalUser++;
850 
851       _psAudit->iUserListFlag = LIST_IN_PROGRESS;
852     }
853     else
854     {
855       /* reset list */
856       _psAudit->pGlobalUser = _psAudit->pUserFile;
857       _pUser = NULL;
858     }
859 
860     writeError(ERR_DEBUG, "[findNextUser] L_FILE User: %s", _pUser);
861   }
862   else if (_psAudit->UserType == L_SINGLE)
863   {
864     if (_pUser != NULL)
865       _pUser = NULL;
866     else
867       _pUser = _psAudit->pGlobalUser;
868   }
869   else
870   {
871     writeError(ERR_FATAL, "[findNextUser] UserType (%d) not properly defined.", _psAudit->UserType);
872   }
873 
874   return _pUser;
875 }
876 
877 /*
878   Return next user-specified password during audit data table building process.
879   This password information is only from the combo file.
880 */
findLocalPass(sAudit * _psAudit)881 char* findLocalPass(sAudit *_psAudit)
882 {
883   char *pPass;
884   char *pComboTmp;
885 
886   if ((_psAudit->PassType == L_COMBO) || (_psAudit->PassType == L_PWDUMP))
887   {
888     /* advance to password */
889     if (_psAudit->pGlobalCombo)
890     {
891       pComboTmp = _psAudit->pComboEntryTmp;
892 
893       while (*pComboTmp != '\0')
894         pComboTmp++;
895       pComboTmp++;
896 
897       while (*pComboTmp != '\0')
898         pComboTmp++;
899       pComboTmp++;
900     }
901 
902     pPass = pComboTmp;
903     writeError(ERR_DEBUG, "[findLocalPass] pPass: %s", pPass);
904   }
905   else
906   {
907     pPass = NULL;
908   }
909 
910   return pPass;
911 }
912 
loadLoginInfo(sAudit * _psAudit)913 int loadLoginInfo(sAudit *_psAudit)
914 {
915   sHost *psHost = NULL;
916   sHost *psHostPrevTmp = NULL;
917   char *pHost = NULL;
918 
919   sUser *psUser = NULL;
920   char *pUser = NULL;
921 
922   sPass *psPass = NULL;
923   char *pPass = NULL;
924 
925   /* initialize / reset */
926   _psAudit->iHostCnt = 0;
927   _psAudit->iHostsDone = 0;
928 
929   while ((pHost = findNextHost(_psAudit, pHost)))
930   {
931     /* combo file: search list to see if host has already been added */
932     psHost = _psAudit->psHostRoot;
933     while (psHost)
934     {
935       if ( strcmp(pHost,psHost->pHost) )
936         psHost = psHost->psHostNext;
937       else
938         break;
939     }
940 
941     /* create new host table in list */
942     if (psHost == NULL)
943     {
944       _psAudit->iHostCnt++;
945       psHost = malloc(sizeof(sHost));
946       memset(psHost, 0, sizeof(sHost));
947 
948       /* set root pointer if this is the first host */
949       if (_psAudit->psHostRoot == NULL)
950       {
951         _psAudit->psHostRoot = psHost;
952         psHostPrevTmp = _psAudit->psHostRoot;
953       }
954       else
955       {
956         psHostPrevTmp->psHostNext = psHost;
957         psHostPrevTmp = psHost;
958       }
959 
960       psHost->pHost = malloc( strlen(pHost) + 1 );
961       memset(psHost->pHost, 0, strlen(pHost) + 1);
962       strncpy(psHost->pHost, pHost, strlen(pHost) + 1);
963       psHost->iPortOverride = _psAudit->iPortOverride;
964       psHost->iUseSSL = _psAudit->iUseSSL;
965       psHost->iTimeout = _psAudit->iTimeout;
966       psHost->iRetryWait = _psAudit->iRetryWait;
967       psHost->iRetries = _psAudit->iRetries;
968       psHost->iUserCnt = 0;
969       psHost->iId = _psAudit->iHostCnt;
970     }
971 
972     while ((pUser = findNextUser(_psAudit, pUser)))
973     {
974       /* combo file: search list to see if user has already been added */
975       psUser = psHost->psUser;
976       while (psUser)
977       {
978         if ( strcmp(pUser,psUser->pUser) )
979           psUser = psUser->psUserNext;
980         else
981           break;
982       }
983 
984       /* create new user table in list */
985       if (psUser == NULL)
986       {
987         psHost->iUserCnt++;
988         psUser = malloc(sizeof(sUser));
989         memset(psUser, 0, sizeof(sUser));
990 
991         if (psHost->psUserPrevTmp)
992         {
993           /* setting host next user pointer */
994           psHost->psUserPrevTmp->psUserNext = psUser;
995         }
996         else
997         {
998           /* setting host root user pointer */
999           psHost->psUser = psUser;
1000         }
1001 
1002         psHost->psUserPrevTmp = psUser;
1003 
1004         psUser->pUser = malloc( strlen(pUser) + 1 );
1005         memset(psUser->pUser, 0, strlen(pUser) + 1);
1006         strncpy(psUser->pUser, pUser, strlen(pUser));
1007         psUser->iPassCnt = _psAudit->iPassCnt;
1008         psUser->iPassStatus = PL_UNSET;
1009         psUser->iId = psHost->iUserCnt;
1010         psHost->iUserPassCnt += _psAudit->iPassCnt;
1011 
1012         if (_psAudit->iPasswordUsernameFlag) {
1013           psHost->iUserPassCnt++;
1014           psUser->iPassCnt++;
1015         }
1016 
1017         if (_psAudit->iPasswordBlankFlag) {
1018           psHost->iUserPassCnt++;
1019           psUser->iPassCnt++;
1020         }
1021       }
1022 
1023       pPass = findLocalPass(_psAudit);
1024       if (pPass)
1025       {
1026         psPass = malloc(sizeof(sPass));
1027         memset(psPass, 0, sizeof(sPass));
1028         psPass->pPass = malloc( strlen(pPass) + 1 );
1029         memset(psPass->pPass, 0, strlen(pPass) + 1);
1030         strncpy(psPass->pPass, pPass, strlen(pPass));
1031         psUser->iPassCnt++;
1032         psHost->iUserPassCnt++;
1033 
1034         if (psUser->psPassPrevTmp)
1035         {
1036           /* setting user next pass pointer */
1037           psUser->psPassPrevTmp->psPassNext = psPass;
1038         }
1039         else
1040         {
1041           /* setting user root pass pointer */
1042           psUser->psPass = psPass;
1043           psUser->psPassCurrent = psPass;
1044         }
1045 
1046         psUser->psPassPrevTmp = psPass;
1047       }
1048     }
1049   }
1050 
1051   return SUCCESS;
1052 }
1053 
1054 
1055 /*
1056   Grab the next password for a particular user
1057 */
getNextPass(sLogin * _psLogin)1058 char* getNextPass(sLogin *_psLogin)
1059 {
1060   sAudit *_psAudit = _psLogin->psServer->psAudit;
1061   sUser *_psUser = _psLogin->psUser;
1062   char *pPass = NULL;
1063 
1064   /* is this user's password list complete? */
1065   if ((_psUser->iPassStatus != PL_DONE) && (_psUser->iPassStatus != PASS_AUDIT_COMPLETE))
1066   {
1067     /* is this the user's first password request? */
1068     if (_psUser->iPassStatus == PL_UNSET)
1069       _psUser->iPassStatus = PL_NULL;
1070 
1071     /* process blank password or password matching username */
1072     if ((_psUser->iPassStatus == PL_NULL) || (_psUser->iPassStatus == PL_USERNAME))
1073     {
1074       if ((_psUser->iPassStatus == PL_NULL) && (_psAudit->iPasswordBlankFlag))
1075       {
1076         pPass = "";
1077         _psUser->iPassStatus = PL_USERNAME;
1078       }
1079       else if (_psAudit->iPasswordUsernameFlag)
1080       {
1081         pPass = _psUser->pUser;
1082         _psUser->iPassStatus = PL_LOCAL;
1083       }
1084       else
1085       {
1086         _psUser->iPassStatus = PL_LOCAL;
1087       }
1088     }
1089 
1090     if (pPass == NULL )
1091     {
1092       /* process local passwords - i.e. passwords specified within combo file for user */
1093       if ((_psUser->iPassStatus == PL_LOCAL) && (_psUser->psPassCurrent))
1094       {
1095         pPass = _psUser->psPassCurrent->pPass;
1096         _psUser->psPassCurrent = _psUser->psPassCurrent->psPassNext;
1097       }
1098       /* process global passwords - i.e. passwords specified via "-p" or "-P" options */
1099       else if (_psAudit->pGlobalPass)
1100       {
1101         _psUser->iPassStatus = PL_GLOBAL;
1102 
1103         if (_psUser->pPass)
1104         {
1105           while (*_psUser->pPass != '\0')
1106             _psUser->pPass++;
1107           _psUser->pPass++;
1108 
1109           if (*_psUser->pPass != '\0')
1110           {
1111             pPass = _psUser->pPass;
1112           }
1113           else
1114           {
1115             /* password auditing of host is complete */
1116             _psUser->iPassStatus = PL_DONE;
1117             _psLogin->psServer->psHost->iUsersDone++;
1118           }
1119         }
1120         else
1121         {
1122           _psUser->pPass = _psAudit->pGlobalPass;
1123           pPass = _psUser->pPass;
1124         }
1125       }
1126       else
1127       {
1128          /* password auditing of host is complete */
1129         _psUser->iPassStatus = PL_DONE;
1130         _psLogin->psServer->psHost->iUsersDone++;
1131       }
1132     }
1133   }
1134 
1135   return pPass;
1136 }
1137 
1138 
1139 /*
1140   Generates the next credential set for login module to test. The module is
1141   responsible for allocating and releasing memory used for the credential set.
1142 */
getNextNormalCredSet(sLogin * _psLogin,sCredentialSet * _psCredSet)1143 int getNextNormalCredSet(sLogin *_psLogin, sCredentialSet *_psCredSet)
1144 {
1145   int nUserListChecked = FALSE;
1146 
1147   _psCredSet->iStatus = CREDENTIAL_SAME_USER;
1148 
1149   /* is this the first user for a login thread? */
1150   if (_psLogin->psUser == NULL)
1151   {
1152     writeError(ERR_DEBUG, "[getNextNormalCred] Initial credential set request for login module.");
1153 
1154     _psLogin->psServer->psHost->iUserStatus = UL_NORMAL;
1155     _psCredSet->iStatus = CREDENTIAL_NEW_USER;
1156 
1157     /* multiple login threads of same user */
1158     if (_psLogin->psServer->psAudit->iParallelLoginFlag == PARALLEL_LOGINS_PASSWORD)
1159     {
1160       if (_psLogin->psServer->psHost->psUserCurrent == NULL)
1161         _psLogin->psServer->psHost->psUserCurrent = _psLogin->psServer->psHost->psUser;
1162 
1163       _psLogin->psUser = _psLogin->psServer->psHost->psUserCurrent;
1164 
1165       if (_psLogin->psUser)
1166         writeError(ERR_DEBUG, "[getNextNormalCred] (PARALLEL_LOGINS_PASSWORD) setting user: %s", _psLogin->psUser->pUser);
1167     }
1168     /* multiple login threads of one unique user per thread */
1169     else
1170     {
1171       /* only increment user pointer if this is not the first module */
1172       if (_psLogin->psServer->psHost->psUserCurrent == NULL)
1173       {
1174         writeError(ERR_DEBUG, "[getNextNormalCred] Assigning initial user for host being tested.");
1175         _psLogin->psServer->psHost->psUserCurrent = _psLogin->psServer->psHost->psUser;
1176         _psLogin->psUser = _psLogin->psServer->psHost->psUserCurrent;
1177         //_psLogin->psServer->psHost->iUserStatus = UL_NORMAL;
1178       }
1179       else
1180       {
1181         writeError(ERR_DEBUG, "[getNextNormalCred] Assigning next available user for host being tested.");
1182         _psLogin->psUser = _psLogin->psServer->psHost->psUserCurrent->psUserNext;
1183         _psLogin->psServer->psHost->psUserCurrent = _psLogin->psUser;
1184       }
1185 
1186       if (_psLogin->psUser)
1187         writeError(ERR_DEBUG, "[getNextNormalCred] (PARALLEL_LOGINS_USER) setting NEW user: %s", _psLogin->psUser->pUser);
1188     }
1189   }
1190 
1191   /* find next available password - if password list is exhausted for user, move on to the next user */
1192   while ((_psLogin->psUser) && ((_psCredSet->pPass = getNextPass(_psLogin)) == NULL))
1193   {
1194     /* is password testing for user complete */
1195     if ((_psLogin->psUser->iPassStatus == PL_DONE) || (_psLogin->psUser->iPassStatus == PASS_AUDIT_COMPLETE))
1196     {
1197       writeError(ERR_INFO, "Login Module: %d - Current user password list is complete, selecting next user.", _psLogin->iId);
1198 
1199       if (_psLogin->psServer->psHost->psUserCurrent == NULL)
1200       {
1201         _psLogin->psUser = NULL;
1202       }
1203       else
1204       {
1205         _psLogin->psUser = _psLogin->psServer->psHost->psUserCurrent->psUserNext;
1206         _psLogin->psServer->psHost->psUserCurrent = _psLogin->psUser;
1207       }
1208 
1209       if (_psLogin->psUser == NULL)
1210       {
1211         /* end of list - check entire list for unfinished credentials */
1212         if (nUserListChecked == FALSE)
1213         {
1214           writeError(ERR_INFO, "Login Module: %d - Current user password list is complete, rescanning userlist for unfinished credentials.", _psLogin->iId);
1215           _psLogin->psUser = _psLogin->psServer->psHost->psUser;
1216           _psLogin->psServer->psHost->psUserCurrent = _psLogin->psUser;
1217           nUserListChecked = TRUE;
1218         }
1219         else
1220         {
1221           writeError(ERR_INFO, "Login Module: %d - No more user accounts available for testing.", _psLogin->iId);
1222           _psCredSet->iStatus = CREDENTIAL_DONE;
1223         }
1224       }
1225       else
1226       {
1227         writeError(ERR_INFO, "Login Module: %d - Selecting next password for user: %s", _psLogin->iId, _psLogin->psUser->pUser);
1228         _psCredSet->iStatus = CREDENTIAL_NEW_USER;
1229       }
1230     }
1231   }
1232 
1233   if ((_psLogin->psUser == NULL) || (_psCredSet->pPass == NULL))
1234   {
1235     //writeError(ERR_INFO, "Login Module: %d - No more available users/passwords, setting credential status to CREDENTIAL_DONE.", _psLogin->iId);
1236     writeError(ERR_INFO, "Login Module: %d - No more users/passwords available in the normal queue.", _psLogin->iId);
1237     //_psCredSet->iStatus = CREDENTIAL_DONE;
1238     _psLogin->psServer->psHost->iUserStatus = UL_MISSED;
1239   }
1240 
1241   _psCredSet->psUser = _psLogin->psUser;
1242 
1243   return SUCCESS;
1244 }
1245 
1246 /*
1247   In certain situations we need to scale back the number of concurrent
1248   login threads targetting a specific service. For example, MSDE's workload
1249   governor limits the service to no more than 5 concurrent connections. If
1250   the user kicked-off 10 parallel login threads, 5 of those are going to
1251   fail and terminate. The challenge is that each of those threads was
1252   already assigned a credential set to test.
1253 
1254   The addMissedCredSet() function creates a linked list of credentials
1255   which were not tested for a given host. This function retrieves the
1256   next credential set from that list for testing.
1257 */
getNextMissedCredSet(sLogin * _psLogin,sCredentialSet * _psCredSet)1258 int getNextMissedCredSet(sLogin *_psLogin, sCredentialSet *_psCredSet)
1259 {
1260   sCredentialSet *psCredSetMissed = NULL;
1261 
1262   writeError(ERR_DEBUG, "Retrieving the next available credential set from list of previously missed sets.");
1263 
1264   /* skip credential if user testing is complete (e.g. password found, account locked) */
1265   psCredSetMissed = _psLogin->psServer->psCredentialSetMissedCurrent;
1266   while ((psCredSetMissed) && (psCredSetMissed->psUser->iPassStatus == PASS_AUDIT_COMPLETE))
1267   {
1268     psCredSetMissed = _psLogin->psServer->psCredentialSetMissedCurrent->psCredentialSetNext;
1269     _psLogin->psServer->psCredentialSetMissedCurrent = psCredSetMissed;
1270   }
1271 
1272   /* located next credential set that was not previously tested */
1273   if (psCredSetMissed)
1274   {
1275     _psCredSet->psUser = psCredSetMissed->psUser;
1276     _psCredSet->pPass = psCredSetMissed->pPass;
1277     _psLogin->psServer->psCredentialSetMissedCurrent = psCredSetMissed->psCredentialSetNext;
1278 
1279     if (_psLogin->psUser == _psCredSet->psUser)
1280       _psCredSet->iStatus = CREDENTIAL_SAME_USER;
1281     else
1282       _psCredSet->iStatus = CREDENTIAL_NEW_USER;
1283 
1284     _psLogin->psServer->iCredentialsMissed--;
1285 
1286     writeError(ERR_DEBUG, "Login Module: %d - Selected next credential set from list of previously missed sets (%s/%s).", _psLogin->iId, _psCredSet->psUser->pUser, _psCredSet->pPass);
1287   }
1288   else
1289   {
1290     writeError(ERR_INFO, "Login Module: %d - No additional missed users/passwords, setting credential status to CREDENTIAL_DONE.", _psLogin->iId);
1291     _psCredSet->iStatus = CREDENTIAL_DONE;
1292     _psLogin->psServer->psHost->iUserStatus = UL_DONE;
1293   }
1294 
1295   _psLogin->psUser = _psCredSet->psUser;
1296 
1297   return SUCCESS;
1298 }
1299 
1300 /*
1301   Function returns next available username and password to module for testing.
1302   The normal host's list of users and their respective passwords (local, global, etc)
1303   are tested first. If any credential sets were not successfully tested (module
1304   instance died for some reason) they re-checked after all normal tests are done.
1305 */
getNextCredSet(sLogin * _psLogin,sCredentialSet * _psCredSet)1306 int getNextCredSet(sLogin *_psLogin, sCredentialSet *_psCredSet)
1307 {
1308   if (_psCredSet == NULL)
1309     writeError(ERR_FATAL, "getNextCredSet() called, but not supplied allocated memory for _psCredSet");
1310 
1311   memset(_psCredSet, 0, sizeof(sCredentialSet));
1312   pthread_mutex_lock(&_psLogin->psServer->ptmMutex);
1313 
1314   /* terminate all login threads */
1315   if (_psLogin->psServer->psAudit->iStatus == AUDIT_ABORT)
1316   {
1317     writeError(ERR_INFO, "Audit aborting... notifying login module: %d", _psLogin->iId);
1318     _psCredSet->iStatus = CREDENTIAL_DONE;
1319   }
1320   /* valid credential set found -- exit host flag set */
1321   else if ((_psLogin->psServer->iValidPairFound) && (_psLogin->psServer->psAudit->iFoundPairExitFlag == FOUND_PAIR_EXIT_HOST))
1322   {
1323     writeError(ERR_INFO, "Exiting Login Module: %d [Stop Host Scan After Valid Pair Found Enabled]", _psLogin->iId);
1324     _psCredSet->iStatus = CREDENTIAL_DONE;
1325   }
1326   /* valid credential set found -- exit audit flag set */
1327   else if ((_psLogin->psServer->psAudit->iValidPairFound) && (_psLogin->psServer->psAudit->iFoundPairExitFlag == FOUND_PAIR_EXIT_AUDIT))
1328   {
1329     writeError(ERR_INFO, "Exiting Login Module: %d [Stop Audit Scans After Valid Pair Found Enabled]", _psLogin->iId);
1330     _psCredSet->iStatus = CREDENTIAL_DONE;
1331   }
1332   else
1333   {
1334     switch (_psLogin->psServer->psHost->iUserStatus)
1335     {
1336       case UL_UNSET:
1337       case UL_NORMAL:
1338         /* check for next available login to perform */
1339         if (getNextNormalCredSet(_psLogin, _psCredSet) != SUCCESS)
1340           writeError(ERR_FATAL, "getNextNormalCredSet() function call failed.");
1341 
1342         /* the normal queue is exhausted - check the missed credentials queue */
1343         if (_psLogin->psServer->psHost->iUserStatus == UL_MISSED)
1344           if (getNextMissedCredSet(_psLogin, _psCredSet) != SUCCESS)
1345             writeError(ERR_FATAL, "getNextMissedCredSet() function call failed.");
1346 
1347         break;
1348       case UL_MISSED:
1349         /* check for next available login missed during normal testing */
1350         if (getNextMissedCredSet(_psLogin, _psCredSet) != SUCCESS)
1351           writeError(ERR_FATAL, "getNextMissedCredSet() function call failed.");
1352         break;
1353       case UL_DONE:
1354         writeError(ERR_INFO, "Login Module: %d - No additional users/passwords, setting credential status to CREDENTIAL_DONE.", _psLogin->iId);
1355         _psCredSet->iStatus = CREDENTIAL_DONE;
1356         break;
1357       default:
1358         writeError(ERR_DEBUG, "Login Module: %d - Entered undefined state (%d) within getNextCredSet()", _psLogin->iId, _psLogin->psServer->psHost->iUserStatus);
1359         break;
1360     }
1361   }
1362 
1363   pthread_mutex_unlock(&_psLogin->psServer->ptmMutex);
1364 
1365   return SUCCESS;
1366 }
1367 
1368 /*
1369   Process password result from login module
1370 */
setPassResult(sLogin * _psLogin,char * _pPass)1371 void setPassResult(sLogin *_psLogin, char *_pPass)
1372 {
1373   pthread_mutex_lock(&_psLogin->psServer->ptmMutex);
1374 
1375   writeVerbose(VB_CHECK,
1376                "[%s] Host: %s (%d of %d, %d complete) User: %s (%d of %d, %d complete) Password: %s (%d of %d complete)",
1377                _psLogin->psServer->psAudit->pModuleName,
1378                _psLogin->psServer->psHost->pHost,
1379                _psLogin->psServer->psHost->iId,
1380                _psLogin->psServer->psAudit->iHostCnt,
1381                _psLogin->psServer->psAudit->iHostsDone,
1382                _psLogin->psUser->pUser,
1383                _psLogin->psUser->iId,
1384                _psLogin->psServer->psHost->iUserCnt,
1385                _psLogin->psServer->psHost->iUsersDone,
1386                _pPass,
1387                _psLogin->psUser->iLoginsDone + 1,
1388                _psLogin->psUser->iPassCnt
1389               );
1390 
1391   _psLogin->iLoginsDone++;
1392   _psLogin->psUser->iLoginsDone++,
1393   _psLogin->psServer->iLoginsDone++;
1394 
1395   switch (_psLogin->iResult)
1396   {
1397   case LOGIN_RESULT_SUCCESS:
1398     if (_psLogin->pErrorMsg) {
1399       writeVerbose(VB_FOUND, "[%s] Host: %s User: %s Password: %s [SUCCESS (%s)]", _psLogin->psServer->psAudit->pModuleName, _psLogin->psServer->psHost->pHost, _psLogin->psUser->pUser, _pPass, _psLogin->pErrorMsg);
1400       free(_psLogin->pErrorMsg);
1401       _psLogin->pErrorMsg = NULL;
1402     }
1403     else
1404       writeVerbose(VB_FOUND, "[%s] Host: %s User: %s Password: %s [SUCCESS]", _psLogin->psServer->psAudit->pModuleName, _psLogin->psServer->psHost->pHost, _psLogin->psUser->pUser, _pPass);
1405 
1406     _psLogin->psServer->psAudit->iValidPairFound = TRUE;
1407     _psLogin->psServer->iValidPairFound = TRUE;
1408     _psLogin->psUser->iPassStatus = PASS_AUDIT_COMPLETE;
1409     _psLogin->psServer->psHost->iUsersDone++;
1410     break;
1411   case LOGIN_RESULT_FAIL:
1412     if (_psLogin->pErrorMsg) {
1413       writeError(ERR_INFO, "[%s] Host: %s User: %s [FAILED (%s)]", _psLogin->psServer->psAudit->pModuleName, _psLogin->psServer->psHost->pHost, _psLogin->psUser->pUser, _psLogin->pErrorMsg);
1414       free(_psLogin->pErrorMsg);
1415       _psLogin->pErrorMsg = NULL;
1416     }
1417     else
1418       writeError(ERR_INFO, "[%s] Host: %s User: %s [FAILED]", _psLogin->psServer->psAudit->pModuleName, _psLogin->psServer->psHost->pHost, _psLogin->psUser->pUser);
1419 
1420     break;
1421   case LOGIN_RESULT_ERROR:
1422     if (_psLogin->pErrorMsg) {
1423       writeVerbose(VB_FOUND, "[%s] Host: %s User: %s Password: %s [ERROR (%s)]", _psLogin->psServer->psAudit->pModuleName, _psLogin->psServer->psHost->pHost, _psLogin->psUser->pUser, _pPass, _psLogin->pErrorMsg);
1424       free(_psLogin->pErrorMsg);
1425       _psLogin->pErrorMsg = NULL;
1426     }
1427     else
1428       writeVerbose(VB_FOUND, "[%s] Host: %s User: %s Password: %s [ERROR]", _psLogin->psServer->psAudit->pModuleName, _psLogin->psServer->psHost->pHost, _psLogin->psUser->pUser, _pPass);
1429 
1430     _psLogin->psUser->iPassStatus = PASS_AUDIT_COMPLETE;
1431     _psLogin->psServer->psHost->iUsersDone++;
1432     break;
1433   default:
1434     writeError(ERR_INFO, "[%s] Host: %s User: %s [UNKNOWN %d]", _psLogin->psServer->psAudit->pModuleName, _psLogin->psServer->psHost->pHost, _psLogin->psUser->pUser, _psLogin->iResult);
1435     break;
1436   }
1437 
1438   pthread_mutex_unlock(&_psLogin->psServer->ptmMutex);
1439 }
1440 
1441 
1442 /*
1443   In certain situations we need to scale back the number of concurrent
1444   login threads targetting a specific service. For example, MSDE's workload
1445   governor limits the service to no more than 5 concurrent connections. If
1446   the user kicked-off 10 parallel login threads, 5 of those are going to
1447   fail and terminate. The challenge is that each of those threads was
1448   already assigned a credential set to test. This function creates a
1449   list of those credentials so that they can be tested by the remaining
1450   threads at the end of their current run.
1451 */
addMissedCredSet(sLogin * _psLogin,sCredentialSet * _psCredSet)1452 int addMissedCredSet(sLogin *_psLogin, sCredentialSet *_psCredSet)
1453 {
1454   sCredentialSet *psCredSetMissed = NULL;
1455 
1456   pthread_mutex_lock(&_psLogin->psServer->ptmMutex);
1457 
1458   writeError(ERR_NOTICE, "[%s] Host: %s - Login thread (%d) prematurely ended. The current number of parallel login threads may exceed what this service can reasonably handle. The total number of threads for this host will be decreased.",
1459                _psLogin->psServer->psAudit->pModuleName,
1460                _psLogin->psServer->psHost->pHost,
1461                _psLogin->iId
1462             );
1463 
1464   if (_psLogin->psServer->iLoginCnt > 1)
1465     _psLogin->psServer->iLoginCnt--;
1466 
1467   writeError(ERR_NOTICE, "[%s] Host: %s User: %s Password: %s - The noted credentials have been added to the end of the queue for testing.",
1468                _psLogin->psServer->psAudit->pModuleName,
1469                _psLogin->psServer->psHost->pHost,
1470                _psCredSet->psUser->pUser,
1471                _psCredSet->pPass
1472             );
1473 
1474   /* build structure for missed credential set */
1475   psCredSetMissed = malloc(sizeof(sCredentialSet));
1476   memset(psCredSetMissed, 0, sizeof(sCredentialSet));
1477 
1478   psCredSetMissed->psUser = _psCredSet->psUser;
1479 
1480   psCredSetMissed->pPass = malloc(strlen(_psCredSet->pPass) + 1);
1481   memset(psCredSetMissed->pPass, 0, strlen(_psCredSet->pPass) + 1);
1482   strncpy(psCredSetMissed->pPass, _psCredSet->pPass, strlen(_psCredSet->pPass));
1483 
1484   /* append structure to host's list of missed credentials */
1485   if (_psLogin->psServer->psCredentialSetMissed == NULL) /* first missed credential set */
1486   {
1487     _psLogin->psServer->psCredentialSetMissed = psCredSetMissed;
1488     _psLogin->psServer->psCredentialSetMissedCurrent = psCredSetMissed;
1489   }
1490   else
1491     _psLogin->psServer->psCredentialSetMissedTail->psCredentialSetNext = psCredSetMissed;
1492 
1493   _psLogin->psServer->psCredentialSetMissedTail = psCredSetMissed;
1494 
1495   _psLogin->psServer->iCredentialsMissed++;
1496 
1497   pthread_mutex_unlock(&_psLogin->psServer->ptmMutex);
1498 
1499   return SUCCESS;
1500 }
1501 
1502 
startModule(void * pParams)1503 void startModule(void* pParams)
1504 {
1505   int64_t nRet = 0;
1506   sModuleStart* modParams = (sModuleStart*)pParams;
1507   if (NULL == modParams)
1508   {
1509     writeError(ERR_FATAL, "Bad pointer passed to invokeModule");
1510     return;
1511   }
1512 
1513   writeError(ERR_DEBUG, "startModule iId: %d pLogin: %X modParams->argv: %X modParams: %X", modParams->pLogin->iId, modParams->pLogin, modParams->argv, modParams);
1514 
1515   nRet = invokeModule(modParams->szModuleName, modParams->pLogin, modParams->argc, modParams->argv);
1516   if (nRet < 0)
1517     writeVerbose(VB_EXIT, "invokeModule failed - see previous errors for an explanation");
1518 
1519   return;
1520 }
1521 
1522 
1523 /*
1524   Initiate and manage host-specific thread pool for logins. Each target host
1525   has a single thread for this purpose. The thread spawns multiple child
1526   threads which each initiate the selected module to perform the actual logons.
1527 */
startLoginThreadPool(void * arg)1528 void startLoginThreadPool(void *arg)
1529 {
1530   sServer *_psServer = (sServer *)arg;
1531   thr_pool_t *login_pool = NULL;
1532   sLogin psLogin[_psServer->psAudit->iLoginCnt];
1533   sModuleStart modParams[_psServer->psAudit->iLoginCnt];
1534   int iLoginId = 0;
1535   int iLoginCnt = _psServer->psAudit->iLoginCnt;
1536 
1537   struct addrinfo hints, *res;
1538   int errcode;
1539   void *ptr;
1540 
1541   writeError(ERR_DEBUG_SERVER, "Server ID: %d Host: %s iUserPassCnt: %d iLoginCnt: %d", _psServer->iId, _psServer->psHost->pHost, _psServer->psHost->iUserPassCnt, iLoginCnt);
1542 
1543   /* create thread pool - min threads, max threads, linger time, attributes */
1544   if (iLoginCnt > _psServer->psHost->iUserPassCnt)
1545     iLoginCnt = _psServer->psHost->iUserPassCnt;
1546 
1547   if ((login_pool = thr_pool_create(0, iLoginCnt, POOL_THREAD_LINGER, NULL)) == NULL)
1548   {
1549     writeError(ERR_FATAL, "Failed to create root login thread pool for host: %s", _psServer->psHost->pHost);
1550   }
1551 
1552   /* resolve host name */
1553   _psServer->pHostIP = malloc(100);
1554   memset(_psServer->pHostIP, 0, 100);
1555 
1556   memset(&hints, 0, sizeof (hints));
1557   hints.ai_family = PF_UNSPEC;
1558   hints.ai_socktype = SOCK_STREAM;
1559   hints.ai_flags |= AI_CANONNAME;
1560 
1561   errcode = getaddrinfo(_psServer->psHost->pHost, NULL, &hints, &res);
1562   if (errcode != 0)
1563   {
1564     writeError(ERR_CRITICAL, "Failed to resolve hostname: %s - %s", _psServer->psHost->pHost, gai_strerror(errcode));
1565     return;
1566   }
1567 
1568   if (res->ai_next != NULL)
1569     writeError(ERR_ERROR, "Hostname resolved to multiple addresses. Selecting first address for testing.");
1570 
1571   inet_ntop (res->ai_family, res->ai_addr->sa_data, _psServer->pHostIP, 100);
1572 
1573   switch (res->ai_family)
1574   {
1575     case AF_INET:
1576       ptr = &((struct sockaddr_in *) res->ai_addr)->sin_addr;
1577       break;
1578     case AF_INET6:
1579       ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
1580       break;
1581   }
1582 
1583   inet_ntop (res->ai_family, ptr, _psServer->pHostIP, 100);
1584   writeError(ERR_DEBUG_SERVER, "Set IPv%d address: %s (%s)",res->ai_family == PF_INET6 ? 6 : 4, _psServer->pHostIP, res->ai_canonname);
1585   freeaddrinfo(res);
1586 
1587   /* add login tasks to pool queue */
1588   for (iLoginId = 0; iLoginId < iLoginCnt; iLoginId++)
1589   {
1590     writeError(ERR_DEBUG_SERVER, "Adding new login task (%d) to server queue (%d)", iLoginId, _psServer->iId);
1591 
1592     psLogin[iLoginId].iId = iLoginId;
1593     psLogin[iLoginId].psServer = _psServer;
1594     psLogin[iLoginId].iResult = LOGIN_RESULT_UNKNOWN;
1595     psLogin[iLoginId].pErrorMsg = NULL;
1596     psLogin[iLoginId].iLoginsDone = 0;
1597     psLogin[iLoginId].psUser = NULL;
1598 
1599     modParams[iLoginId].szModuleName = szModuleName;
1600     modParams[iLoginId].pLogin = &(psLogin[iLoginId]); //psLogin + (iLoginId * sizeof(sLogin));
1601     modParams[iLoginId].argc = nModuleParamCount;
1602     modParams[iLoginId].argv = (char**)arrModuleParams;
1603 
1604     if ( thr_pool_queue(login_pool, startModule, (void *) &modParams[iLoginId]) < 0 )
1605     {
1606       writeError(ERR_CRITICAL, "Failed to add module launch task to login thread pool for server queue: %d.", _psServer->iId);
1607       return;
1608     }
1609   }
1610 
1611   /* wait for login thread pool to finish */
1612   writeError(ERR_DEBUG_SERVER, "waiting for server %d login pool to end", _psServer->iId);
1613   thr_pool_wait(login_pool);
1614 
1615   /*
1616     In certain situations we need to scale back the number of concurrent
1617     login threads targetting a specific service. For example, MSDE's workload
1618     governor limits the service to no more than 5 concurrent connections. If
1619     the user kicked-off 10 parallel login threads, 5 of those are going to
1620     fail and terminate. The challenge is that each of those threads was
1621     already assigned a credential set to test.
1622 
1623     When these threads failed, we pushed the missed credentials into a queue
1624     assigned to the target host. This queue may already have been taken care of
1625     by running threads when they finished their normal tasks. However, if the
1626     missed logons were pushed to the queue by exiting threads after the other
1627     threads had terminated, they are still sitting there. To deal with this
1628     problem, we kick off a single thread to run through these.
1629   */
1630   iLoginId = 0;
1631   if ((_psServer->psAudit->iStatus != AUDIT_ABORT) && (_psServer->iCredentialsMissed > 0))
1632   {
1633     writeError(ERR_DEBUG_SERVER, "Adding new clean-up login task to server queue (%d) for %d missed logins", _psServer->iId, _psServer->iCredentialsMissed);
1634 
1635     _psServer->psHost->iUserStatus = UL_MISSED;
1636     psLogin[iLoginId].iResult = LOGIN_RESULT_UNKNOWN;
1637     psLogin[iLoginId].pErrorMsg = NULL;
1638     psLogin[iLoginId].psUser = NULL;
1639 
1640     if ( thr_pool_queue(login_pool, startModule, (void *) &modParams[iLoginId]) < 0 )
1641     {
1642       writeError(ERR_CRITICAL, "Failed to add module launch task to login thread pool for server queue: %d.", _psServer->iId);
1643       return;
1644     }
1645 
1646     /* wait for login thread pool to finish */
1647     writeError(ERR_DEBUG_SERVER, "waiting for server %d login pool to end", _psServer->iId);
1648     thr_pool_wait(login_pool);
1649   }
1650 
1651   writeError(ERR_DEBUG_SERVER, "destroying server %d login pool", _psServer->iId);
1652   thr_pool_destroy(login_pool);
1653 
1654   /* track the number of hosts which have been completed */
1655   pthread_mutex_lock(&_psServer->psAudit->ptmMutex);
1656   _psServer->psAudit->iHostsDone++;
1657   pthread_mutex_unlock(&_psServer->psAudit->ptmMutex);
1658 
1659   /* The logon modules for server have all terminated, however, the server's userlist is not marked
1660      as completed. This may be due to the module exiting prematurely (e.g. the service being tested
1661      became unavailable). We mark the host as UL_ERROR to avoid having it added to the resume list.
1662   */
1663   if ((_psServer->psAudit->iStatus != AUDIT_ABORT) && ((_psServer->psHost->iUserStatus == UL_NORMAL) || (_psServer->psHost->iUserStatus == UL_MISSED)))
1664   {
1665      writeError(ERR_DEBUG_SERVER, "Server thread exiting and server's userlist testing was marked as in progress. Was this host prematurely aborted?");
1666     _psServer->psHost->iUserStatus = UL_ERROR;
1667   }
1668 
1669   writeError(ERR_DEBUG_SERVER, "exiting server: %d", _psServer->iId);
1670 
1671   free(_psServer->pHostIP);
1672 
1673   return;
1674 }
1675 
1676 
1677 /*
1678   Initiate and manage thread pool for target systems. Each target host
1679   will have a single parent thread, which manages all childs login threads
1680   specific to that individual machine.
1681 */
startServerThreadPool(sAudit * _psAudit)1682 int startServerThreadPool(sAudit *_psAudit)
1683 {
1684   sServer psServer[_psAudit->iHostCnt];
1685   sHost *psHost;
1686   int iServerId;
1687 
1688   sUser *psUser;
1689   char *szResumeMap = NULL;
1690   char *szUserMap = NULL;
1691   int nAddHost;
1692   int nUserMapSize;
1693   int nFirstNewHostFound;
1694   int nFirstNewUserFound;
1695   char szTmp[11];
1696   char szTmp1[11];
1697   char szTmp2[11];
1698 
1699   writeVerbose(VB_GENERAL, "Parallel Hosts: %d Parallel Logins: %d", _psAudit->iServerCnt, _psAudit->iLoginCnt);
1700 
1701   writeVerbose(VB_GENERAL, "Total Hosts: %d ", _psAudit->iHostCnt);
1702   if (_psAudit->iUserCnt == 0) writeVerbose(VB_GENERAL, "Total Users: [combo]");
1703   else writeVerbose(VB_GENERAL, "Total Users: %d", _psAudit->iUserCnt);
1704   if (_psAudit->iPassCnt == 0) writeVerbose(VB_GENERAL, "Total Passwords: [combo]");
1705   else writeVerbose(VB_GENERAL, "Total Passwords: %d", _psAudit->iPassCnt);
1706 
1707   /* create thread pool - min threads, max threads, linger time, attributes */
1708   if (_psAudit->iServerCnt > _psAudit->iHostCnt)
1709     _psAudit->iServerCnt = _psAudit->iHostCnt;
1710 
1711   /* initialize global crypto (OpenSSL, Libgcrypt) variables */
1712   init_crypto_locks();
1713 
1714   if ((_psAudit->server_pool = thr_pool_create(0, _psAudit->iServerCnt, POOL_THREAD_LINGER, NULL)) == NULL)
1715   {
1716     writeError(ERR_ERROR, "Failed to create root server thread pool.");
1717     return FAILURE;
1718   }
1719 
1720   /* initialize servers */
1721   memset(psServer, 0, sizeof(sServer) * _psAudit->iHostCnt);
1722   psHost = _psAudit->psHostRoot;
1723 
1724   nFirstNewHostFound = FALSE;
1725 
1726   /* add server tasks to pool queue (one task per host to be tested) */
1727   for (iServerId = 0; iServerId < _psAudit->iHostCnt; iServerId++)
1728   {
1729     /* resume map was supplied by user - skip hosts and users which were previously completed */
1730     nAddHost = TRUE;
1731     if (_psAudit->pOptResume)
1732     {
1733       memset(szTmp, 0, 11);
1734       memset(szTmp1, 0, 11);
1735       snprintf(szTmp, 10, "h%d.", psHost->iId);
1736       snprintf(szTmp1, 10, "h%du", psHost->iId);
1737 
1738       if (nFirstNewHostFound == TRUE)
1739       {
1740         writeError(ERR_DEBUG_SERVER, "[Host Resume] Adding host: %d (we've passed the point of the previous run)", psHost->iId);
1741       }
1742       else if ((szResumeMap = strstr(_psAudit->pOptResume, szTmp1)))
1743       {
1744         writeError(ERR_DEBUG_SERVER, "[Host Resume] Adding host: %d (host was located in resume map)", psHost->iId);
1745 
1746         /* extract host's user resume map */
1747         if (index(szResumeMap + 1, 0x68))
1748           nUserMapSize = index(szResumeMap + 1, 0x68) - szResumeMap; /* calculate length of host resume map from start to the next "h" */
1749         else if (index(szResumeMap + 1, 0x2e))
1750           nUserMapSize = index(szResumeMap + 1, 0x2e) - szResumeMap; /* calculate length of host resume map from start to the terminating "." */
1751         else
1752           nUserMapSize = strlen(szResumeMap); /* single, or last, host resume */
1753 
1754         if (nUserMapSize < 4)
1755           writeError(ERR_FATAL, "Error extacting user resume map for host: %d", psHost->iId);
1756 
1757         szUserMap = malloc(nUserMapSize + 1);
1758         memset(szUserMap, 0, nUserMapSize + 1);
1759         strncpy(szUserMap, szResumeMap, nUserMapSize);
1760         writeError(ERR_DEBUG_SERVER, "[Host Resume] Host: %d - Processing host's user resume map: %s", psHost->iId, szUserMap);
1761 
1762         /* examine each user for the host and mark previously tested accounts as completed */
1763         nFirstNewUserFound = FALSE;
1764         psUser = psHost->psUser;
1765         while (psUser)
1766         {
1767           memset(szTmp, 0, 11);
1768           memset(szTmp1, 0, 11);
1769           snprintf(szTmp, 10, "u%du", psUser->iId);
1770           snprintf(szTmp1, 10, "u%dh", psUser->iId);
1771           snprintf(szTmp2, 10, "u%d.", psUser->iId);
1772 
1773           if (nFirstNewUserFound == TRUE)
1774           {
1775             writeError(ERR_DEBUG_SERVER, "[User Resume] Adding user: %d (we've passed the point of the previous run)", psUser->iId);
1776           }
1777           else if (strstr(szResumeMap, szTmp))
1778           {
1779             writeError(ERR_DEBUG_SERVER, "[User Resume] Adding user: %d (user was located in resume map)", psUser->iId);
1780           }
1781           else if ((strstr(szResumeMap, szTmp1)) || (strstr(szResumeMap, szTmp2)))
1782           {
1783             writeError(ERR_DEBUG_SERVER, "[User Resume] Adding user: %d (user was located in resume map and identified as first untouched account)", psUser->iId);
1784             nFirstNewUserFound = TRUE;
1785           }
1786           else
1787           {
1788             writeError(ERR_DEBUG_SERVER, "[User Resume] Skipping user: %d (user has already been tested)", psUser->iId);
1789             psUser->iPassStatus = PL_DONE;
1790           }
1791 
1792           psUser = psUser->psUserNext;
1793         }
1794       }
1795       else if (strstr(_psAudit->pOptResume, szTmp))
1796       {
1797         writeError(ERR_DEBUG_SERVER, "[Host Resume] Adding host: %d (host was located in resume map and identified as first untouched system)", psHost->iId);
1798         nFirstNewHostFound = TRUE;
1799       }
1800       else
1801       {
1802         writeError(ERR_DEBUG_SERVER, "[Host Resume] Skipping host: %d (host has already been tested)", psHost->iId);
1803         nAddHost = FALSE;
1804         psHost->iUserStatus = UL_DONE;
1805       }
1806     }
1807 
1808     if (nAddHost)
1809     {
1810       writeError(ERR_DEBUG_AUDIT, "adding new server (%d) to queue", iServerId);
1811 
1812       if (pthread_mutex_init(&(psServer[iServerId].ptmMutex), NULL) != 0)
1813         writeError(ERR_FATAL, "Server (%d) mutex initialization failed - %s\n", iServerId, strerror( errno ) );
1814 
1815       psServer[iServerId].psAudit = _psAudit;
1816       psServer[iServerId].iId = iServerId;
1817       psServer[iServerId].psHost = psHost;
1818       psServer[iServerId].iLoginCnt = _psAudit->iLoginCnt;
1819       psServer[iServerId].iLoginsDone = 0;
1820       psServer[iServerId].iCredentialsMissed = 0;
1821 
1822       if ( thr_pool_queue(_psAudit->server_pool, startLoginThreadPool, (void *) &psServer[iServerId]) < 0 )
1823       {
1824         writeError(ERR_ERROR, "Failed to add host task to server thread pool.");
1825         return FAILURE;
1826       }
1827     }
1828 
1829     psHost = psHost->psHostNext;
1830   }
1831 
1832   /* wait for thread pool to finish */
1833   writeError(ERR_DEBUG_AUDIT, "waiting for server pool to end");
1834   thr_pool_wait(_psAudit->server_pool);
1835   writeError(ERR_DEBUG_AUDIT, "destroying server pool");
1836   thr_pool_destroy(_psAudit->server_pool);
1837 
1838   /* destroy and clean-up server objects */
1839   for (iServerId = 0; iServerId < _psAudit->iHostCnt; iServerId++)
1840   {
1841     if (pthread_mutex_init(&(psServer[iServerId].ptmMutex), NULL) != 0)
1842       writeError(ERR_FATAL, "Server (%d) mutex destroy call failed - %s\n", iServerId, strerror( errno ) );
1843   }
1844 
1845   kill_crypto_locks();
1846 
1847   return SUCCESS;
1848 }
1849 
1850 /*
1851   Function called on SIGINT. We process the host and user tables and generate
1852   a map representing their current state. This map can then be supplied to
1853   Medusa to essentially resume the run. It should be noted, however, that users
1854   which were partially tested will be resumed from the start of their password
1855   list.
1856 */
sigint_handler(int sig)1857 void sigint_handler(int sig __attribute__((unused)))
1858 {
1859   sHost *psHost;
1860   sUser *psUser;
1861   char szTmp[10+1]; // we can only resume h + 7 + . + \0, so 7 digits... 9,999,999 (should be enough) hosts
1862   char *szResumeMap = NULL;
1863   int nResumeMapSize = 0;
1864   int nItemByteSize = 0;
1865   struct sigaction sig_action;
1866 
1867   /* SIGINT is blocked by default within the handler. We explicitly unblock it here.
1868      This allows us to hit CTRL-C a second time and really quit the application
1869      without waiting for the threads to complete their work.
1870   */
1871   sig_action.sa_flags = 0;
1872   sigemptyset(&sig_action.sa_mask);
1873   sigaddset(&sig_action.sa_mask, SIGINT);
1874   sig_action.sa_handler = SIG_DFL;
1875   sigaction(SIGINT, &sig_action, 0);
1876   sigprocmask(SIG_UNBLOCK, &sig_action.sa_mask, 0);
1877 
1878   /* notify threads that they should be exiting and then wait for them to finish */
1879   writeError(ERR_ALERT, "Medusa received SIGINT - Sending notification to login threads that we are are aborting.");
1880   psAudit->iStatus = AUDIT_ABORT;
1881 
1882   writeError(ERR_INFO, "Waiting for login threads to terminate...");
1883   thr_pool_wait(psAudit->server_pool);
1884 
1885   /*
1886     We note each partially finished host and the first new host for which
1887     testing has not started. We do the same for each partially completed
1888     host's user list. The number of partially completed hosts likely
1889     matches the number of parallel hosts being tested (T). The number of
1890     partially completed users for a given host likely matches the number
1891     of parallel logins being performed (t). This results in us reporting
1892     T(t + 1) + 1 items. Let's assume each item will require X bytes to
1893     report, which leads us to X(Tt + T + 1) bytes needed.
1894 
1895     Example: h6u1u2h7u3u4h8.
1896              +---------------- First host which was not 100% completed
1897                +-------------- First user for host which was not 100% completed
1898                  +------------ First user for host which was not started
1899                          +---- First host which was not started
1900   */
1901 
1902   /* base our byte count on the largest number we may need to record - ex: h1236\0 */
1903   if (psAudit->iHostCnt > psAudit->iUserCnt)
1904     nItemByteSize = 1 + (int)log10(psAudit->iHostCnt) + 1;
1905   else
1906     nItemByteSize = 1 + (int)log10(psAudit->iUserCnt) + 1;
1907 
1908   nResumeMapSize = nItemByteSize * (psAudit->iServerCnt * psAudit->iLoginCnt + psAudit->iServerCnt + 1) + 1; /* include terminating "." */
1909   szResumeMap = malloc(nResumeMapSize + 1);
1910   memset(szResumeMap, 0, nResumeMapSize + 1);
1911   memset(szTmp, 0, 10 + 1);
1912 
1913   psHost = psAudit->psHostRoot;
1914   while ((psHost) && (psHost->iUserStatus != UL_UNSET))
1915   {
1916     /* identify the hosts which are not 100% complete */
1917     if ((psHost->iUserStatus != UL_DONE) && (psHost->iUserStatus != UL_ERROR))
1918     {
1919       writeError(ERR_DEBUG, "Incomplete Host: %d", psHost->iId);
1920       memset(szTmp, 0, 10 + 1);
1921       snprintf(szTmp, 10, "h%d", psHost->iId);
1922       strncat(szResumeMap, szTmp, 10);
1923 
1924       /* identify the users which are not 100% complete for specific host */
1925       psUser = psHost->psUser;
1926       while ((psUser) && (psUser->iPassStatus != PL_UNSET))
1927       {
1928         if ((psUser->iPassStatus == PL_DONE) || (psUser->iPassStatus == PASS_AUDIT_COMPLETE))
1929           writeError(ERR_DEBUG, "Complete User: %d", psUser->iId);
1930         else
1931         {
1932           writeError(ERR_DEBUG, "Incomplete User: %d", psUser->iId);
1933           memset(szTmp, 0, 10 + 1);
1934           snprintf(szTmp, 10, "u%d", psUser->iId);
1935           strncat(szResumeMap, szTmp, 10);
1936         }
1937 
1938         psUser = psUser->psUserNext;
1939       }
1940 
1941       /* identify the first untouched user */
1942       if ((psUser) && (psUser->iPassStatus == PL_UNSET))
1943       {
1944         writeError(ERR_DEBUG, "First New User: %d", psUser->iId);
1945         memset(szTmp, 0, 10 + 1);
1946         snprintf(szTmp, 10, "u%d", psUser->iId);
1947         strncat(szResumeMap, szTmp, 10);
1948       }
1949     }
1950     else
1951     {
1952       writeError(ERR_DEBUG, "Complete Host: %d", psHost->iId);
1953     }
1954 
1955     psHost = psHost->psHostNext;
1956   }
1957 
1958   /* identify the first untouched host */
1959   if ((psHost) && (psHost->iUserStatus == UL_UNSET))
1960   {
1961     writeError(ERR_DEBUG, "First New Host: %d", psHost->iId);
1962     memset(szTmp, 0, 10 + 1);
1963     snprintf(szTmp, 8, "h%d", psHost->iId);
1964     strncat(szResumeMap, szTmp, 8);
1965   }
1966 
1967   /* terminate resume map */
1968   strncat(szResumeMap, ".", 1);
1969 
1970   writeError(ERR_ALERT, "To resume scan, add the following to your original command: \"-Z %s\"", szResumeMap);
1971 
1972   free(szResumeMap);
1973 
1974   exit(0);
1975 }
1976 
main(int argc,char ** argv,char * envp[])1977 int main(int argc, char **argv, char *envp[] __attribute__((unused)))
1978 {
1979   struct sigaction sig_action;
1980   int iExitStatus = EXIT_SUCCESS;
1981   int i;
1982 
1983   struct tm *tm_ptr;
1984   time_t the_time;
1985   char time_buf[256];
1986 
1987   /* set signal handling for SIGINT */
1988   sig_action.sa_flags = 0;
1989   sigemptyset(&sig_action.sa_mask);
1990   sigaddset(&sig_action.sa_mask, SIGINT);
1991   sig_action.sa_handler = sigint_handler;
1992   sigaction(SIGINT, &sig_action, 0);
1993 
1994   /* initial module settings and parameters
1995      Don't worry if there are NULL or blank values here
1996      (they will be checked when loading the module)
1997   */
1998   szModuleName = NULL;
1999   szModulePaths[0] = getenv("MEDUSA_MODULE_PATH");
2000   szModulePaths[1] = ".";
2001 #ifdef DEFAULT_MOD_PATH
2002   szModulePaths[2] = DEFAULT_MOD_PATH;
2003 #else
2004   szModulePaths[2] = "/usr/lib/medusa/modules";
2005 #endif
2006 
2007   szTempModuleParam = NULL;
2008   arrModuleParams = malloc(sizeof(char*));
2009   memset(arrModuleParams, 0, sizeof(char*));
2010   nModuleParamCount = 0;
2011 
2012   /* initialized audit structure */
2013   psAudit = malloc(sizeof(sAudit));
2014   memset(psAudit, 0, sizeof(sAudit));
2015 
2016   if (pthread_mutex_init(&(psAudit->ptmMutex), NULL) != 0)
2017     writeError(ERR_FATAL, "Audit mutex initialization failed - %s\n", strerror( errno ) );
2018 
2019   /* parse user-supplied parameters - populate module parameters */
2020   if (checkOptions(argc, argv, psAudit))
2021   {
2022     usage();
2023     exit(EXIT_FAILURE);
2024   }
2025 
2026   for (i = 0; i < nModuleParamCount; i++)
2027   {
2028     writeVerbose(VB_GENERAL, "Module parameter: %s", arrModuleParams[i]);
2029   }
2030 
2031   if (szModuleName == NULL)
2032   {
2033     writeVerbose(VB_EXIT, "You must specify a module to execute using -M MODULE_NAME");
2034     freeModuleParams();
2035     exit(EXIT_FAILURE);
2036   }
2037 
2038   if (psAudit->HostType == L_FILE)
2039   {
2040     loadFile(psAudit->pOptHost, &psAudit->pHostFile, &psAudit->iHostCnt);
2041     psAudit->pGlobalHost = psAudit->pHostFile;
2042   }
2043 
2044   if (psAudit->UserType == L_FILE)
2045   {
2046     loadFile(psAudit->pOptUser, &psAudit->pUserFile, &psAudit->iUserCnt);
2047     psAudit->pGlobalUser = psAudit->pUserFile;
2048   }
2049 
2050   if (psAudit->PassType == L_FILE)
2051   {
2052     loadFile(psAudit->pOptPass, &psAudit->pPassFile, &psAudit->iPassCnt);
2053     psAudit->pGlobalPass = psAudit->pPassFile;
2054   }
2055 
2056   if (psAudit->pOptCombo != NULL)
2057   {
2058     loadFile(psAudit->pOptCombo, &psAudit->pComboFile, &psAudit->iComboCnt);
2059     psAudit->pGlobalCombo = psAudit->pComboFile;
2060     if (processComboFile(&psAudit))
2061     {
2062       exit(iExitStatus);
2063     }
2064   }
2065 
2066   if ( loadLoginInfo(psAudit) == SUCCESS )
2067     writeError(ERR_DEBUG, "Successfully loaded login information.");
2068   else
2069     writeError(ERR_FATAL, "Failed to load login information.");
2070 
2071   if (psAudit->pOptCombo != NULL) free(psAudit->pComboFile);
2072   if (psAudit->pHostFile != NULL) free(psAudit->pHostFile);
2073   if (psAudit->pUserFile != NULL) free(psAudit->pUserFile);
2074 
2075   if (psAudit->pOptOutput != NULL)
2076   {
2077     if ((pOutputFile = fopen(psAudit->pOptOutput, "a+")) == NULL)
2078     {
2079       writeError(ERR_FATAL, "Failed to open output file %s - %s", psAudit->pOptOutput, strerror( errno ) );
2080     }
2081     else
2082     {
2083       if (pthread_mutex_init((&ptmFileMutex), NULL) != 0)
2084         writeError(ERR_FATAL, "File mutex initialization failed - %s\n", strerror( errno ) );
2085 
2086       /* write start time and user options to log */
2087       (void) time(&the_time);
2088       tm_ptr = localtime(&the_time);
2089       strftime(time_buf, 256, "%Y-%m-%d %H:%M:%S", tm_ptr);
2090       writeVerbose(VB_NONE_FILE, "# Medusa v.%s (%s)\n", VERSION, time_buf);
2091       writeVerbose(VB_NONE_FILE, "# ");
2092 
2093       for (i =0; i < argc; i++)
2094       {
2095         writeVerbose(VB_NONE_FILE, "%s ", argv[i]);
2096       }
2097       writeVerbose(VB_NONE_FILE, "\n");
2098     }
2099   }
2100 
2101   /* launch actually password auditing threads */
2102   if ( startServerThreadPool(psAudit) == SUCCESS )
2103   {
2104     /* stop time */
2105     (void) time(&the_time);
2106     tm_ptr = localtime(&the_time);
2107     strftime(time_buf, 256, "%Y-%m-%d %H:%M:%S", tm_ptr);
2108 
2109     writeVerbose(VB_NONE_FILE, "# Medusa has finished (%s).\n", time_buf);
2110     writeVerbose(VB_GENERAL, "Medusa has finished.");
2111     iExitStatus = EXIT_SUCCESS;
2112   }
2113   else
2114   {
2115     /* stop time */
2116     (void) time(&the_time);
2117     tm_ptr = localtime(&the_time);
2118     strftime(time_buf, 256, "%Y-%m-%d %H:%M:%S", tm_ptr);
2119 
2120     writeVerbose(VB_NONE_FILE, "# Medusa failed (%s).\n", time_buf);
2121     writeError(ERR_CRITICAL, "Medusa failed.");
2122     iExitStatus = EXIT_FAILURE;
2123   }
2124 
2125   /* general memory clean-up */
2126   if ((psAudit->pOptOutput != NULL) && (pthread_mutex_destroy(&ptmFileMutex) != 0))
2127     writeError(ERR_FATAL, "File mutex destroy call failed - %s\n", strerror( errno ) );
2128 
2129   if (pthread_mutex_destroy(&(psAudit->ptmMutex)) != 0)
2130     writeError(ERR_FATAL, "Audit mutex destroy call failed - %s\n", strerror( errno ) );
2131 
2132   free(psAudit->pPassFile);
2133   free(psAudit);
2134 
2135   if (szModuleName != NULL)
2136     free(szModuleName);
2137 
2138   freeModuleParams();
2139 
2140   exit(iExitStatus);
2141 }
2142