--------------------- PatchSet 1433 Date: 2001/01/30 11:01:07 Author: kinkie Branch: auth_rewrite Tag: (none) Log: Removed stale comments. Added last-ditch authentication. Indented. Improved arguments parsing - buggy squid reload/reconfigure code might cause the helper to spin uncontrollably. Members: src/auth/ntlm/helpers/NTLMSSP/ntlm_auth.c:1.1.2.4->1.1.2.5 Index: squid/src/auth/ntlm/helpers/NTLMSSP/ntlm_auth.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/auth/ntlm/helpers/NTLMSSP/Attic/ntlm_auth.c,v retrieving revision 1.1.2.4 retrieving revision 1.1.2.5 diff -u -r1.1.2.4 -r1.1.2.5 --- squid/src/auth/ntlm/helpers/NTLMSSP/ntlm_auth.c 11 Jan 2001 06:37:05 -0000 1.1.2.4 +++ squid/src/auth/ntlm/helpers/NTLMSSP/ntlm_auth.c 30 Jan 2001 11:01:07 -0000 1.1.2.5 @@ -12,24 +12,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * - * Warning! We MIGHT be open to buffer overflows caused by malformed headers - * - * DONE list: - * use hashtable to cache authentications. Yummy performance-boost, security - * loss should be negligible for two reasons: - * - if they-re using NT, there's no security to speak of anyways - * - it can't get worse than basic authentication. - * cache expiration - * challenge hash expiry and renewal. - * PDC disconnect, after X minutes of inactivity - * - * TODO list: - * change syntax from options-driven to args-driven, with args domain - * or domain[/\]server, and an arbitrary number of backup Domain Controllers - * we don't really need the "status" management, it's more for debugging - * purposes. Remove it. - * Maybe we can cache the created challenge, saving more time? - * */ @@ -38,6 +20,11 @@ #include "ntlm.h" #include "util.h" +/* these are part of rfcnb-priv.h and smblib-priv.h */ +extern int RFCNB_errno; +extern int SMB_Get_Error_Msg (int msg, char *msgbuf, int len); +extern int SMB_Get_Last_Error (); + #define BUFFER_SIZE 10240 #if HAVE_STDLIB_H @@ -58,45 +45,72 @@ #include #endif -char load_balance = 0, failover_enabled = 0, protocol_pedantic = 0; +char load_balance = 0, failover_enabled = 0, protocol_pedantic = 0, last_ditch_enabled = 0; dc *controllers = NULL; int numcontrollers = 0; dc *current_dc; +char smb_error_buffer[1000]; + /* housekeeping cycle and periodic operations */ static unsigned char need_dc_resurrection = 0; static void -resurrect_dead_dc() +resurrect_dead_dc () { - int j; - dc *c = controllers; + int j; + dc *c = controllers; - need_dc_resurrection = 0; - for (j = 0; j < numcontrollers; j++) - if (c->status != DC_OK && is_dc_ok(c->domain, c->controller)) - c->status = DC_OK; + need_dc_resurrection = 0; + for (j = 0; j < numcontrollers; j++) + if (c->status != DC_OK && is_dc_ok (c->domain, c->controller)) + c->status = DC_OK; } /* makes a null-terminated string upper-case. Changes CONTENTS! */ static void -uc(char *string) +uc (char *string) { - char *p = string, c; - while ((c = *p)) { - *p = toupper(c); - p++; + char *p = string, c; + while ((c = *p)) + { + *p = toupper (c); + p++; } } /* makes a null-terminated string lower-case. Changes CONTENTS! */ static void -lc(char *string) +lc (char *string) { - char *p = string, c; - while ((c = *p)) { - *p = tolower(c); - p++; + char *p = string, c; + while ((c = *p)) + { + *p = tolower (c); + p++; + } +} + + +void +send_bh_or_ld (char *bhmessage, ntlm_authenticate * failedauth, int authlen) +{ + char *creds = NULL; + if (last_ditch_enabled) + { + creds = fetch_credentials (failedauth, authlen); + if (creds) + { + SEND2 ("LD %s", creds); + } + else + { + SEND ("NA last-ditch on, but no credentials"); + } + } + else + { + SEND (bhmessage); } } @@ -104,255 +118,321 @@ * options: * -b try load-balancing the domain-controllers * -f fail-over to another DC if DC connection fails. + * -l last-ditch-mode * domain\controller ... */ char *my_program_name = NULL; void -usage() +usage () { - fprintf(stderr, - "%s usage:\n" - "%s [-b] [-f] domain\\controller [domain\\controller ...]\n" - "-b, if specified, enables load-balancing among controllers\n" - "-f, if specified, enables failover among controllers\n\n" - "You MUST specify at least one Domain Controller.\n" - "You can use either \\ or / as separator between the domain name \n" - "\tand the controller name\n" - ,my_program_name - ,my_program_name); + fprintf (stderr, + "%s usage:\n" + "%s [-b] [-f] domain\\controller [domain\\controller ...]\n" + "-b, if specified, enables load-balancing among controllers\n" + "-f, if specified, enables failover among controllers\n" + "-l, if specified, changes behavior on domain controller failyures to" + "\tlast-ditch\n\n" "You MUST specify at least one Domain Controller.\n" + "You can use either \\ or / as separator between the domain name \n" + "\tand the controller name\n", + my_program_name, my_program_name); } void -process_options(int argc, char *argv[]) +process_options (int argc, char *argv[]) { - int opt, j, had_error = 0; - dc *new_dc = NULL, *last_dc = NULL; - while (-1 != (opt = getopt(argc, argv, "bf"))) { - switch (opt) { + int opt, j, had_error = 0; + dc *new_dc = NULL, *last_dc = NULL; + while (-1 != (opt = getopt (argc, argv, "bfl"))) + { + switch (opt) + { case 'b': - load_balance = 1; - break; + load_balance = 1; + break; case 'f': - failover_enabled = 1; - break; + failover_enabled = 1; + break; + case 'l': + last_ditch_enabled = 1; + break; default: - fprintf(stderr, "unknown option: -%c. Exiting\n", opt); - usage(); - had_error = 1; - } - } - if (had_error) - exit(1); - /* Okay, now begin filling controllers up */ - /* we can avoid memcpy-ing, and just reuse argv[] */ - for (j = optind; j < argc; j++) { - char *d, *c; - d = argv[j]; - if (NULL == (c = strchr(d, '\\')) && NULL == (c = strchr(d, '/'))) { - fprintf(stderr, "Couldn't grok domain-controller %s\n", d); - continue; - } - *c++ = '\0'; - new_dc = (dc *) malloc(sizeof(dc)); - if (!new_dc) { - fprintf(stderr, "Malloc error while parsing DC options\n"); - continue; - } - /* capitalize */ - uc(c); - uc(d); - numcontrollers++; - new_dc->domain = d; - new_dc->controller = c; - new_dc->status = DC_OK; - if (controllers == NULL) { /* first controller */ - controllers = new_dc; - last_dc = new_dc; - } else { - last_dc->next = new_dc; /* can't be null */ - last_dc = new_dc; - } - } - if (numcontrollers == 0) { - fprintf(stderr, "You must specify at least one domain-controller!\n"); - usage(); - exit(1); + fprintf (stderr, "unknown option: -%c. Exiting\n", opt); + usage (); + had_error = 1; + } } - last_dc->next = controllers; /* close the queue, now it's circular */ + if (had_error) + exit (1); + /* Okay, now begin filling controllers up */ + /* we can avoid memcpy-ing, and just reuse argv[] */ + for (j = optind; j < argc; j++) + { + char *d, *c; + /* d will not be freed in case of non-error. Since we don't reconfigure, + * it's going to live as long as the process anyways */ + d = malloc (strlen (argv[j]) + 1); + strcpy (d, argv[j]); + debug ("Adding domain-controller %s (argv[%d])\n", d, j); + if (NULL == (c = strchr (d, '\\')) && NULL == (c = strchr (d, '/'))) + { + fprintf (stderr, "Couldn't grok domain-controller %s\n", d); + free (d); + continue; + } + /* more than one delimiter is not allowed */ + if (NULL != strchr (c + 1, '\\') || NULL != strchr (c + 1, '/')) + { + fprintf (stderr, "Broken domain-controller %s\n", d); + free (d); + continue; + } + *c++ = '\0'; + new_dc = (dc *) malloc (sizeof (dc)); + if (!new_dc) + { + fprintf (stderr, "Malloc error while parsing DC options\n"); + free (d); + continue; + } + /* capitalize */ + uc (c); + uc (d); + numcontrollers++; + new_dc->domain = d; + new_dc->controller = c; + new_dc->status = DC_OK; + if (controllers == NULL) + { /* first controller */ + controllers = new_dc; + last_dc = new_dc; + } + else + { + last_dc->next = new_dc; /* can't be null */ + last_dc = new_dc; + } + } + if (numcontrollers == 0) + { + fprintf (stderr, "You must specify at least one domain-controller!\n"); + usage (); + exit (1); + } + last_dc->next = controllers; /* close the queue, now it's circular */ } /* tries connecting to the domain controllers in the "controllers" ring, * with failover if the adequate option is specified. */ const char * -obtain_challenge() +obtain_challenge () { - int j = 0; - const char *ch; - for (j = 0; j < numcontrollers; j++) { - if (current_dc->status == DC_OK) { - ch = make_challenge(current_dc->domain, current_dc->controller); - if (ch) - return ch; /* All went OK, returning */ - /* Huston, we've got a problem. Take this DC out of the loop */ - current_dc->status = DC_DEAD; - need_dc_resurrection = 1; - } - if (failover_enabled == 0) /* No failover. Just return */ - return NULL; - /* Try with the next */ - current_dc = current_dc->next; + int j = 0; + const char *ch; + debug ("obtain_challenge: getting new challenge\n"); + for (j = 0; j < numcontrollers; j++) + { + if (current_dc->status == DC_OK) + { + debug ("getting challenge from %s\%s\n", current_dc->domain, current_dc->controller); + ch = make_challenge (current_dc->domain, current_dc->controller); + if (ch) + return ch; /* All went OK, returning */ + /* Huston, we've got a problem. Take this DC out of the loop */ + debug ("Marking DC as DEAD\n"); + current_dc->status = DC_DEAD; + need_dc_resurrection = 1; + } + if (failover_enabled == 0) /* No failover. Just return */ + return NULL; + /* Try with the next */ + current_dc = current_dc->next; } - return NULL; + /* DC (all DCs if failover is enabled) failed. */ + return NULL; } void -manage_request() +manage_request () { - ntlmhdr *fast_header; - char buf[10240]; - const char *ch; - char *ch2, *decoded, *cred; - int plen; - - if (fgets(buf, BUFFER_SIZE, stdin) == NULL) - exit(0); /* BIIG buffer */ - ch2 = memchr(buf, '\n', BUFFER_SIZE); /* safer against overrun than strchr */ - if (ch2) { - *ch2 = '\0'; /* terminate the string at newline. */ - ch = ch2; - } - debug("ntlm authenticator. Got '%s' from Squid\n", buf); - - if (memcmp(buf, "KK ", 3) == 0) { /* authenticate-request */ - /* figure out what we got */ - decoded = base64_decode(buf + 3); - /* Note: we don't need to manage memory at this point, since - * base64_decode returns a pointer to static storage. - */ - - if (!decoded) { /* decoding failure, return error */ - SEND("NA Packet format error, couldn't base64-decode"); - return; - } - /* fast-track-decode request type. */ - fast_header = (struct _ntlmhdr *) decoded; - - /* sanity-check: it IS a NTLMSSP packet, isn't it? */ - if (memcmp(fast_header->signature, "NTLMSSP", 8) != 0) { - SEND("NA Broken authentication packet"); - return; + ntlmhdr *fast_header; + char buf[BUFFER_SIZE]; + const char *ch; + char *ch2, *decoded, *cred; + int plen; + + if (fgets (buf, BUFFER_SIZE, stdin) == NULL) + exit (0); /* BIIG buffer */ + ch2 = memchr (buf, '\n', BUFFER_SIZE); /* safer against overrun than strchr */ + if (ch2) + { + *ch2 = '\0'; /* terminate the string at newline. */ + ch = ch2; + } + debug ("ntlm authenticator. Got '%s' from Squid\n", buf); + + if (memcmp (buf, "KK ", 3) == 0) + { /* authenticate-request */ + /* figure out what we got */ + decoded = base64_decode (buf + 3); + /* Note: we don't need to manage memory at this point, since + * base64_decode returns a pointer to static storage. + */ + + if (!decoded) + { /* decoding failure, return error */ + SEND ("NA Packet format error, couldn't base64-decode"); + return; + } + /* fast-track-decode request type. */ + fast_header = (struct _ntlmhdr *) decoded; + + /* sanity-check: it IS a NTLMSSP packet, isn't it? */ + if (memcmp (fast_header->signature, "NTLMSSP", 8) != 0) + { + SEND ("NA Broken authentication packet"); + return; } - switch (fast_header->type) { + switch (fast_header->type) + { case NTLM_NEGOTIATE: - SEND("NA Invalid negotiation request received"); - return; - /* notreached */ + SEND ("NA Invalid negotiation request received"); + return; + /* notreached */ case NTLM_CHALLENGE: - SEND("NA Got a challenge. We refuse to have our authority disputed"); - return; - /* notreached */ + SEND ("NA Got a challenge. We refuse to have our authority disputed"); + return; + /* notreached */ case NTLM_AUTHENTICATE: - /* check against the DC */ - plen = strlen(buf) * 3 / 4; /* we only need it here. Optimization */ - cred = ntlm_check_auth((ntlm_authenticate *) decoded, plen); - if (cred == NULL) { - switch (ntlm_errno) { + /* check against the DC */ + plen = strlen (buf) * 3 / 4; /* we only need it here. Optimization */ + cred = ntlm_check_auth ((ntlm_authenticate *) decoded, plen); + if (cred == NULL) + { + switch (ntlm_errno) + { case NTLM_LOGON_ERROR: - SEND("NA authentication failure"); - dc_disconnect(); - current_dc = current_dc->next; - return; + SEND ("NA authentication failure"); + /* I must have been drugged when I wrote the following two lines */ + /* dc_disconnect(); + current_dc = current_dc->next; */ + return; case NTLM_SERVER_ERROR: - SEND("BH Domain Controller Error"); - dc_disconnect(); + send_bh_or_ld ("BH Domain Controller Error", (ntlm_authenticate *) decoded, plen); + /* SEND("BH Domain Controller Error"); */ + /* we don't really need to disconnect NOW. + Besides, we asked squid to force a reconnect. This way, if we + have authentications in flight, we might even succeed. + */ + /* dc_disconnect(); */ + + SMB_Get_Error_Msg (SMB_Get_Last_Error (), smb_error_buffer, 1000); + debug ("Last error was: %s, RFC errno=%d\n", smb_error_buffer, RFCNB_errno); + if (failover_enabled) current_dc = current_dc->next; - return; + return; case NTLM_PROTOCOL_ERROR: - SEND("BH Domain Controller communication error"); - dc_disconnect(); + send_bh_or_ld ("BH Domain Controller communication error", (ntlm_authenticate *) decoded, plen); + /* SEND("BH Domain Controller communication error"); */ + /* dc_disconnect(); */ + if (failover_enabled) current_dc = current_dc->next; - return; + return; case NTLM_NOT_CONNECTED: - SEND("BH Domain Controller (or network) died on us"); - dc_disconnect(); + send_bh_or_ld ("BH Domain Controller (or network) died on us", (ntlm_authenticate *) decoded, plen); + /* SEND("BH Domain Controller (or network) died on us"); */ + /* dc_disconnect(); */ + if (failover_enabled) current_dc = current_dc->next; - return; + return; case NTLM_BAD_PROTOCOL: - SEND("BH Domain controller failure"); - dc_disconnect(); + send_bh_or_ld ("BH Domain controller failure", (ntlm_authenticate *) decoded, plen); + /* SEND("BH Domain controller failure"); */ +/* dc_disconnect(); *//* maybe we're overreacting? */ + SMB_Get_Error_Msg (SMB_Get_Last_Error (), smb_error_buffer, 1000); + debug ("Last error was: %s. RFCNB errno=%d\n", smb_error_buffer, RFCNB_errno); + if (failover_enabled) current_dc = current_dc->next; - return; + return; default: - SEND("BH Unhandled error while talking to Domain Controller"); - dc_disconnect(); + send_bh_or_ld ("BH Unhandled error while talking to Domain Controller", (ntlm_authenticate *) decoded, plen); + /* SEND("BH Unhandled error while talking to Domain Controller"); */ +/* dc_disconnect(); *//* maybe we're overreacting? */ + if (failover_enabled) current_dc = current_dc->next; - return; + return; } } - lc(cred); /* let's lowercase them for our convenience */ - SEND2("AF %s", cred); - return; + lc (cred); /* let's lowercase them for our convenience */ + SEND2 ("AF %s", cred); + return; default: - SEND("BH unknown authentication packet type"); - return; + SEND ("BH unknown authentication packet type"); + return; } - return; + return; } - if (memcmp(buf, "YR", 2) == 0) { /* refresh-request */ - dc_disconnect(); - ch = obtain_challenge(); - while (ch == NULL) { - sleep(30); - ch = obtain_challenge(); + if (memcmp (buf, "YR", 2) == 0) + { /* refresh-request */ + dc_disconnect (); + ch = obtain_challenge (); + /* Robert says we can afford to wait forever. I'll trust him on this + one */ + while (ch == NULL) + { + sleep (30); + ch = obtain_challenge (); } - SEND2("TT %s", ch); - if (need_dc_resurrection) /* looks like a good moment... */ - resurrect_dead_dc(); - return; + SEND2 ("TT %s", ch); + if (need_dc_resurrection) /* looks like a good moment... */ + resurrect_dead_dc (); + return; } - SEND("BH Helper detected protocol error"); - return; + SEND ("BH Helper detected protocol error"); + return; /********* END ********/ } int -main(int argc, char *argv[]) +main (int argc, char *argv[]) { - debug("starting up...\n"); + debug ("ntlm_auth build " __DATE__ ", " __TIME__ " starting up...\n"); - my_program_name = argv[0]; - process_options(argc, argv); + my_program_name = argv[0]; + process_options (argc, argv); - debug("options processed OK\n"); + debug ("options processed OK\n"); - /* initialize FDescs */ - setbuf(stdout, NULL); - setbuf(stderr, NULL); - - /* select the first domain controller we're going to use */ - current_dc = controllers; - if (load_balance != 0 && numcontrollers > 1) { - int n; - pid_t pid = getpid(); - n = pid % numcontrollers; - debug("load balancing. Selected controller #%d\n", n); - while (n > 0) { - current_dc = current_dc->next; - n--; - } - } - while (1) { - debug("managing request\n"); - manage_request(); + /* initialize FDescs */ + setbuf (stdout, NULL); + setbuf (stderr, NULL); + + /* select the first domain controller we're going to use */ + current_dc = controllers; + if (load_balance != 0 && numcontrollers > 1) + { + int n; + pid_t pid = getpid (); + n = pid % numcontrollers; + debug ("load balancing. Selected controller #%d\n", n); + while (n > 0) + { + current_dc = current_dc->next; + n--; + } + } + while (1) + { + debug ("managing request\n"); + manage_request (); } - return 0; + return 0; }