Preserve bare backslashes in AF and TT NTLM/Negotiate helper responses. Bare backslashes separate authentication domain and user name in AF. When authentication helper protocol was enhanced to support annotations, we started to use strwordtok() to parse AF and TT responses, but strwordtok() eats bare backslashes (and does other decoding that might not be safe in AF and AT context?). We now use a yet another custom parser to extract and skip words. I started with reusing strtok() for that, but was too worried that some other code (e.g., annotations parser) might use it at the same time, resulting in conflicts. Besides, strtok() requires hacks when we do not want to tokenize the whole thing. === modified file 'src/HelperReply.cc' --- src/HelperReply.cc 2012-11-28 01:13:21 +0000 +++ src/HelperReply.cc 2013-02-21 01:26:00 +0000 @@ -1,92 +1,122 @@ /* * DEBUG: section 84 Helper process maintenance * AUTHOR: Amos Jeffries */ #include "squid.h" #include "ConfigParser.h" #include "HelperReply.h" #include "helper.h" #include "rfc1738.h" #include "SquidString.h" HelperReply::HelperReply(char *buf, size_t len) : result(HelperReply::Unknown), whichServer(NULL) { parse(buf,len); } +/** Like strtok(start, " ") but does not use a static area that makes strtok() + * unsafe when multiple callers might call strtok() in one context. Also + * advances its argument to the next word, so that the strtok(NULL, "") hack + * is not required at the end when we do not want to extract all words. + * Does NOT decode unlike strwordtok() and so is safe for bare backslashes. + * TODO: Make w_space a parameter? Move elsewhere? + */ +static char * +SplitWordAndAdvance(char *&start) +{ + if (!start) + return NULL; + + start += strspn(start, w_space); // skip leading whitespace + + if (!*start) + return NULL; // empty string or just whitespace; no words + + char *word = start; + + start += strcspn(start, w_space); // skip first word + + if (*start) { // there is whitespace after the first word (at least) + *start = '\0'; // terminate the first word + ++start; // advance beyond the termination character we just added + } + + return word; +} + void HelperReply::parse(char *buf, size_t len) { // check we have something to parse if (!buf || len < 1) { // for now ensure that legacy handlers are not presented with NULL strings. other_.init(1,1); other_.terminate(); return; } char *p = buf; // optimization: do not consider parsing result code if the response is short. // URL-rewriter may return relative URLs or empty response for a large portion // of its replies. if (len >= 2) { // some helper formats (digest auth, URL-rewriter) just send a data string // we must also check for the ' ' character after the response token (if anything) if (!strncmp(p,"OK",2) && (len == 2 || p[2] == ' ')) { result = HelperReply::Okay; p+=2; } else if (!strncmp(p,"ERR",3) && (len == 3 || p[3] == ' ')) { result = HelperReply::Error; p+=3; } else if (!strncmp(p,"BH",2) && (len == 2 || p[2] == ' ')) { result = HelperReply::BrokenHelper; p+=2; } else if (!strncmp(p,"TT ",3)) { // NTLM challenge token result = HelperReply::TT; p+=3; // followed by an auth token - char *w1 = strwordtok(NULL, &p); + char *w1 = SplitWordAndAdvance(p); if (w1 != NULL) { MemBuf authToken; authToken.init(); authToken.append(w1, strlen(w1)); notes.add("token",authToken.content()); } else { // token field is mandatory on this response code result = HelperReply::BrokenHelper; notes.add("message","Missing 'token' data"); } } else if (!strncmp(p,"AF ",3)) { // NTLM/Negotate OK response result = HelperReply::Okay; p+=3; // followed by: // an optional auth token and user field // or, an optional username field - char *w1 = strwordtok(NULL, &p); - char *w2 = strwordtok(NULL, &p); + char *w1 = SplitWordAndAdvance(p); + char *w2 = SplitWordAndAdvance(p); if (w2 != NULL) { // Negotiate "token user" MemBuf authToken; authToken.init(); authToken.append(w1, strlen(w1)); notes.add("token",authToken.content()); MemBuf user; user.init(); user.append(w2,strlen(w2)); notes.add("user",user.content()); } else if (w1 != NULL) { // NTLM "user" MemBuf user; user.init(); user.append(w1,strlen(w1)); notes.add("user",user.content()); } } else if (!strncmp(p,"NA ",3)) {