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