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() ate bare backslashes (and did other decoding that might not be safe in AF and AT context?). This patch adds an optional decode parameter to strwordtok() to allow the caller to prevent strwordtok() from decoding backslashes and quoted strings. The new parameter is used to decode AF and TT responses. This is an alternative patch. The original patch added a new tokenizer that just split words like strtok() but without bad side effects. === modified file 'src/HelperReply.cc' --- src/HelperReply.cc 2013-02-14 05:42:57 +0000 +++ src/HelperReply.cc 2013-03-15 04:28:02 +0000 @@ -40,61 +40,61 @@ debugs(84, 3, "Buff length is larger than 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] == ' ')) { debugs(84, 3, "helper Result = OK"); result = HelperReply::Okay; p+=2; } else if (!strncmp(p,"ERR",3) && (len == 3 || p[3] == ' ')) { debugs(84, 3, "helper Result = ERR"); result = HelperReply::Error; p+=3; } else if (!strncmp(p,"BH",2) && (len == 2 || p[2] == ' ')) { debugs(84, 3, "helper Result = BH"); 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 = strwordtok(NULL, &p, false); 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 = strwordtok(NULL, &p, false); + char *w2 = strwordtok(NULL, &p, false); 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)) { === modified file 'src/SquidString.h' --- src/SquidString.h 2012-09-22 10:56:48 +0000 +++ src/SquidString.h 2013-03-15 04:32:56 +0000 @@ -167,23 +167,31 @@ size_type len_; /* current length */ char *buf_; _SQUID_INLINE_ void set(char const *loc, char const ch); _SQUID_INLINE_ void cutPointer(char const *loc); }; _SQUID_INLINE_ std::ostream & operator<<(std::ostream& os, String const &aString); _SQUID_INLINE_ bool operator<(const String &a, const String &b); #if _USE_INLINE_ #include "String.cci" #endif const char *checkNullString(const char *p); int stringHasWhitespace(const char *); int stringHasCntl(const char *); -char *strwordtok(char *buf, char **t); + +/** Like strtok(start, " ") but does not use a static area that makes strtok() + * unsafe when multiple callers might call strtok() in one context. Sets next + * argument to the area after the found word, so that the strtok(NULL, "") + * hack is not required at the end when we do not want to extract all words. + * If decode is true, does rudimentary decoding of quoted strings and + * backslashes (recognizing \r and \n sequences). + */ +char *strwordtok(char *buf, char **next, bool decode = true); #endif /* SQUID_STRING_H */ === modified file 'src/String.cc' --- src/String.cc 2012-10-04 09:14:06 +0000 +++ src/String.cc 2013-03-15 01:21:34 +0000 @@ -333,62 +333,63 @@ stringHasCntl(const char *s) { unsigned char c; while ((c = (unsigned char) *s++) != '\0') { if (c <= 0x1f) return 1; if (c >= 0x7f && c <= 0x9f) return 1; } return 0; } /* * Similar to strtok, but has some rudimentary knowledge * of quoting */ char * -strwordtok(char *buf, char **t) +strwordtok(char *buf, char **t, bool decode) { unsigned char *word = NULL; unsigned char *p = (unsigned char *) buf; unsigned char *d; unsigned char ch; int quoted = 0; if (!p) p = (unsigned char *) *t; if (!p) goto error; while (*p && xisspace(*p)) ++p; if (!*p) goto error; word = d = p; + if (decode) { while ((ch = *p)) { switch (ch) { case '\\': ++p; switch (*p) { case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; default: ch = *p; @@ -407,40 +408,52 @@ case '"': quoted = !quoted; ++p; break; default: if (!quoted && xisspace(*p)) { ++p; goto done; } *d = *p; ++d; ++p; break; } } + } else { + while ((ch = *p)) { + if (xisspace(ch)) { + ++p; + goto done; + } + + *d = ch; + ++d; + ++p; + } + } done: *d = '\0'; error: *t = (char *) p; return (char *) word; } const char * checkNullString(const char *p) { return p ? p : "(NULL)"; } const char * String::pos(char const *aString) const { if (undefined()) return NULL;