From: rousskov <>
Date: Sun, 22 Feb 1998 14:45:13 +0000 (+0000)
Subject: - Added "basic" authentication scheme for Cache Manager interface.
X-Git-Tag: SQUID_3_0_PRE1~4037
X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=63259c34e9c29e18b3b5e258268d2108f70e1e44;p=thirdparty%2Fsquid.git
- Added "basic" authentication scheme for Cache Manager interface.
- Added ERR_CACHE_MGR_ACCESS_DENIED page to notify of authentication
failures when accessing Cache Manager.
- Added "-v" (Verbose) and "-H" (extra Headers) options to client.c.
---
diff --git a/ChangeLog b/ChangeLog
index 11392b3a43..39d19ea56f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,23 @@
Future/Current Changes to squid-1.2.beta16 (Feb 23, 1998):
+ - Added "basic" authentication scheme for Cache Manager interface.
+ Tested with Netscape 3.01. When a password protected function is
+ accessed, Squid sends an HTTP_UNAUTHORIZED reply, and the browser
+ prompts the user for "user name" and "password" for the specified
+ action. The user name is currently used for logging purposes only.
+ The password is verified against must be an appropriate
+ "cachemgr_passwd" entry from squid.conf. Note that Netscape will
+ cache your authentication info and will reuse it for future
+ accesses. Caching is done on per-realm (per-action) basis, but
+ Netscape may try to use information from other realms when new realm
+ is accessed. The old interface (appending @password to the url)
+ is still supported but discouraged.
+
+ - Added ERR_CACHE_MGR_ACCESS_DENIED page to notify of authentication
+ failures when accessing Cache Manager.
+
+ - Added "-v" (Verbose) and "-H" (extra Headers) options to client.c.
+
- Added simple context-based debugging to debug.c. Currently, the
context is defined as a constant string. Context reporting is
triggered by debug() calls. Context debugging routines print
diff --git a/errors/English/ERR_CACHE_MGR_ACCESS_DENIED b/errors/English/ERR_CACHE_MGR_ACCESS_DENIED
new file mode 100644
index 0000000000..0e121ea605
--- /dev/null
+++ b/errors/English/ERR_CACHE_MGR_ACCESS_DENIED
@@ -0,0 +1,31 @@
+
+ERROR: Cache Manager Access Denied
+
+
+ERROR
+Cache Manager Access Denied
+
+
+While trying to retrieve the URL:
+%U
+
+The following error was encountered:
+
+-
+
+Cache Manager Access Denied.
+
+
+
+
+Sorry, you are not currently allowed to request:
+
%U
+from this cache manager until you have authenticated yourself.
+
+
+ You need to use Netscape version 2.0 or greater, or Microsoft Internet
+Explorer 3.0, or an HTTP/1.1 compliant browser for this to work. Please
+contact the cache administrator if you have
+difficulties authenticating yourself or, if you are the
+administrator, read Squid documentation on cache manager interface and check
+cache log for more detailed error messages.
diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc
index 5c1fdd82d1..e14509d41d 100644
--- a/src/HttpHeader.cc
+++ b/src/HttpHeader.cc
@@ -1,5 +1,5 @@
/*
- * $Id: HttpHeader.cc,v 1.3 1998/02/21 18:46:32 rousskov Exp $
+ * $Id: HttpHeader.cc,v 1.4 1998/02/22 07:45:15 rousskov Exp $
*
* DEBUG: section 55 HTTP Header
* AUTHOR: Alex Rousskov
@@ -784,6 +784,17 @@ httpHeaderSetStr(HttpHeader *hdr, http_hdr_type id, const char *str)
httpHeaderSet(hdr, id, value);
}
+void
+httpHeaderSetAuth(HttpHeader *hdr, const char *authScheme, const char *realm)
+{
+ MemBuf mb;
+ assert(hdr && authScheme && realm);
+ memBufDefInit(&mb);
+ memBufPrintf(&mb, "%s realm=\"%s\"", authScheme, realm);
+ httpHeaderSetStr(hdr, HDR_WWW_AUTHENTICATE, mb.buf);
+ memBufClean(&mb);
+}
+
/* add extension header (these fields are not parsed/analyzed/joined, etc.) */
void
httpHeaderAddExt(HttpHeader *hdr, const char *name, const char* value)
@@ -955,6 +966,11 @@ httpHeaderEntryClean(HttpHeaderEntry *e) {
httpSccDestroy(e->field.v_pscc);
break;
case ftPExtField:
+ /* tmp check to track a bug @?@ @?@ */
+ if (e->field.v_int == 1) {
+ debug(55,0) ("BUG: attempt to free an invalid HeaderExtField (%p). Ignored.\n",
+ e->field.v_pefield);
+ } else
if (e->field.v_pefield)
httpHeaderExtFieldDestroy(e->field.v_pefield);
break;
diff --git a/src/HttpReply.cc b/src/HttpReply.cc
index 4f8899bfe4..70ac733f64 100644
--- a/src/HttpReply.cc
+++ b/src/HttpReply.cc
@@ -1,5 +1,5 @@
/*
- * $Id: HttpReply.cc,v 1.3 1998/02/21 18:46:33 rousskov Exp $
+ * $Id: HttpReply.cc,v 1.4 1998/02/22 07:45:16 rousskov Exp $
*
* DEBUG: section 58 HTTP Reply (Response)
* AUTHOR: Alex Rousskov
@@ -37,6 +37,7 @@
/* local constants */
/* local routines */
+static void httpReplyDoDestroy(HttpReply *rep);
static int httpReplyParseStep(HttpReply *rep, const char *parse_start, int atEnd);
static int httpReplyParseError(HttpReply *rep);
static int httpReplyIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end);
@@ -78,7 +79,7 @@ httpReplyDestroy(HttpReply *rep)
assert(rep);
tmp_debug(here) ("destroying rep: %p\n", rep);
httpReplyClean(rep);
- memFree(MEM_HTTPREPLY, rep);
+ httpReplyDoDestroy(rep);
}
void
@@ -88,6 +89,17 @@ httpReplyReset(HttpReply *rep)
httpReplyInit(rep);
}
+/* absorb: copy the contents of a new reply to the old one, destroy new one */
+void
+httpReplyAbsorb(HttpReply *rep, HttpReply *new_rep)
+{
+ assert(rep && new_rep);
+ httpReplyClean(rep);
+ *rep = *new_rep;
+ /* cannot use Clean() on new reply now! */
+ httpReplyDoDestroy(new_rep);
+}
+
/* parses a buffer that may not be 0-terminated */
int
httpReplyParse(HttpReply *rep, const char *buf)
@@ -298,6 +310,11 @@ httpReplyHasScc(const HttpReply *rep, http_scc_type type)
/* internal routines */
+/* internal function used by Destroy and Absorb */
+static void
+httpReplyDoDestroy(HttpReply *rep) {
+ memFree(MEM_HTTPREPLY, rep);
+}
/*
* parses a 0-terminating buffer into HttpReply.
diff --git a/src/cache_manager.cc b/src/cache_manager.cc
index 9f9c3e48cb..45bc7a7e30 100644
--- a/src/cache_manager.cc
+++ b/src/cache_manager.cc
@@ -1,6 +1,6 @@
/*
- * $Id: cache_manager.cc,v 1.3 1998/02/21 00:56:50 rousskov Exp $
+ * $Id: cache_manager.cc,v 1.4 1998/02/22 07:45:17 rousskov Exp $
*
* DEBUG: section 16 Cache Manager Objects
* AUTHOR: Duane Wessels
@@ -36,6 +36,7 @@
typedef struct {
StoreEntry *entry;
char *action;
+ char *user_name;
char *passwd;
} cachemgrStateData;
@@ -48,7 +49,12 @@ typedef struct _action_table {
} action_table;
static action_table * cachemgrFindAction(const char *action);
+#if 0
static cachemgrStateData *cachemgrParse(const char *url);
+#else
+static cachemgrStateData *cachemgrParseUrl(const char *url);
+#endif
+static void cachemgrParseHeaders(cachemgrStateData *mgr, const request_t *request);
static int cachemgrCheckPassword(cachemgrStateData *);
static void cachemgrStateFree(cachemgrStateData *mgr);
static char *cachemgrPasswdGet(cachemgr_passwd *, const char *);
@@ -88,7 +94,7 @@ cachemgrFindAction(const char *action)
}
static cachemgrStateData *
-cachemgrParse(const char *url)
+cachemgrParseUrl(const char *url)
{
int t;
LOCAL_ARRAY(char, host, MAX_URL);
@@ -100,15 +106,47 @@ cachemgrParse(const char *url)
if (t < 2) {
xstrncpy(request, "menu", MAX_URL);
} else if ((a = cachemgrFindAction(request)) == NULL) {
- debug(16, 0) ("cachemgrParse: action '%s' not found\n", request);
+ debug(16, 0) ("cachemgrParseUrl: action '%s' not found\n", request);
return NULL;
}
+ /* set absent entries to NULL so we can test if they are present later */
mgr = xcalloc(1, sizeof(cachemgrStateData));
- mgr->passwd = xstrdup(t == 3 ? password : "nopassword");
+ mgr->user_name = NULL;
+ mgr->passwd = t == 3 ? xstrdup(password) : NULL;
mgr->action = xstrdup(request);
return mgr;
}
+static void
+cachemgrParseHeaders(cachemgrStateData *mgr, const request_t *request)
+{
+ const char *basic_cookie; /* base 64 _decoded_ user:passwd pair */
+ const char *authField;
+ const char *passwd_del;
+ assert(mgr && request);
+ /* this parsing will go away when hdrs are added to request_t @?@ */
+ basic_cookie = mime_get_auth(request->headers, "Basic", &authField);
+ debug(16,9) ("cachemgrParseHeaders: got auth: '%s'\n", authField ? authField:"");
+ if (!authField)
+ return;
+ if (!basic_cookie) {
+ debug(16, 1) ("cachemgrParseHeaders: unknown auth format in '%s'\n", authField);
+ return;
+ }
+ if (!(passwd_del = strchr(basic_cookie, ':'))) {
+ debug(16, 1) ("cachemgrParseHeaders: unknown basic_cookie '%s' format in '%s'\n", basic_cookie, authField);
+ return;
+ }
+ /* found user:password pair, reset old values */
+ safe_free(mgr->user_name);
+ safe_free(mgr->passwd);
+ mgr->user_name = xstrdup(basic_cookie);
+ mgr->user_name[passwd_del - basic_cookie] = '\0';
+ mgr->passwd = xstrdup(passwd_del+1);
+ /* warning: this prints decoded password which maybe not what you want to do @?@ @?@ */
+ debug(16,9) ("cachemgrParseHeaders: got user: '%s' passwd: '%s'\n", mgr->user_name, mgr->passwd);
+}
+
/*
* return 0 if mgr->password is good
*/
@@ -124,6 +162,8 @@ cachemgrCheckPassword(cachemgrStateData * mgr)
return 1;
if (strcmp(pwd, "none") == 0)
return 0;
+ if (!mgr->passwd)
+ return 1;
return strcmp(pwd, mgr->passwd);
}
@@ -131,21 +171,19 @@ static void
cachemgrStateFree(cachemgrStateData *mgr)
{
safe_free(mgr->action);
+ safe_free(mgr->user_name);
safe_free(mgr->passwd);
xfree(mgr);
}
void
-cachemgrStart(int fd, StoreEntry * entry)
+cachemgrStart(int fd, request_t *request, StoreEntry * entry)
{
cachemgrStateData *mgr = NULL;
ErrorState *err = NULL;
-#if 0
- char *hdr;
-#endif
action_table *a;
debug(16, 3) ("objectcacheStart: '%s'\n", storeUrl(entry));
- if ((mgr = cachemgrParse(storeUrl(entry))) == NULL) {
+ if ((mgr = cachemgrParseUrl(storeUrl(entry))) == NULL) {
err = errorCon(ERR_INVALID_URL, HTTP_NOT_FOUND);
err->url = xstrdup(storeUrl(entry));
errorAppendEntry(entry, err);
@@ -154,14 +192,42 @@ cachemgrStart(int fd, StoreEntry * entry)
}
mgr->entry = entry;
entry->expires = squid_curtime;
- debug(16, 1) ("CACHEMGR: %s requesting '%s'\n",
+ debug(16, 5) ("CACHEMGR: %s requesting '%s'\n",
fd_table[fd].ipaddr, mgr->action);
+ /* get additional info from request headers */
+ cachemgrParseHeaders(mgr, request);
+ debug(16, 1) ("CACHEMGR: %s (user: %s) requesting '%s'\n",
+ fd_table[fd].ipaddr, mgr->user_name ? mgr->user_name : "",
+ mgr->action);
/* Check password */
if (cachemgrCheckPassword(mgr) != 0) {
+#if 0 /* old response, we ask for authentication now */
cachemgrStateFree(mgr);
debug(16, 1) ("WARNING: Incorrect Cachemgr Password!\n");
err = errorCon(ERR_INVALID_URL, HTTP_NOT_FOUND);
errorAppendEntry(entry, err);
+#else
+ /* build error message */
+ ErrorState *err = errorCon(ERR_CACHE_MGR_ACCESS_DENIED, HTTP_UNAUTHORIZED);
+ HttpReply *rep;
+ /* warn if user specified incorrect password */
+ if (mgr->passwd)
+ debug(16, 1) ("WARNING: CACHEMGR: Incorrect Password (user: %s, action: %s)!\n",
+ mgr->user_name ? mgr->user_name : "", mgr->action);
+ else
+ debug(16, 3) ("CACHEMGR: requesting authentication for action: '%s'.\n",
+ mgr->action);
+ err->request = requestLink(request);
+ rep = errorBuildReply(err);
+ errorStateFree(err);
+ /* add Authenticate header, use 'action' as a realm because password depends on action */
+ httpHeaderSetAuth(&rep->hdr, "Basic", mgr->action);
+ /* move info to the mem_obj->reply */
+ httpReplyAbsorb(entry->mem_obj->reply, rep);
+ /* store the reply */
+ httpReplySwapOut(entry->mem_obj->reply, entry);
+ cachemgrStateFree(mgr);
+#endif
entry->expires = squid_curtime;
storeComplete(entry);
return;
diff --git a/src/client.cc b/src/client.cc
index 0b604a849b..c8782a29a2 100644
--- a/src/client.cc
+++ b/src/client.cc
@@ -1,7 +1,7 @@
/*
- * $Id: client.cc,v 1.55 1998/02/11 03:14:37 wessels Exp $
+ * $Id: client.cc,v 1.56 1998/02/22 07:45:18 rousskov Exp $
*
* DEBUG: section 0 WWW Client
* AUTHOR: Harvest Derived
@@ -124,19 +124,21 @@ static void
usage(const char *progname)
{
fprintf(stderr,
- "Usage: %s [-ars] [-i IMS] [-h host] [-p port] [-m method] [-t count] [-I ping-interval] url\n"
+ "Usage: %s [-arsv] [-i IMS] [-h host] [-p port] [-m method] [-t count] [-I ping-interval] [-H 'strings'] url\n"
"Options:\n"
" -P file PUT request.\n"
" -a Do NOT include Accept: header.\n"
" -r Force cache to reload URL.\n"
" -s Silent. Do not print data to stdout.\n"
+ " -v Verbose. Print outgoing message to stderr.\n"
" -i IMS If-Modified-Since time (in Epoch seconds).\n"
" -h host Retrieve URL from cache on hostname. Default is localhost.\n"
" -p port Port number of cache. Default is %d.\n"
" -m method Request method, default is GET.\n"
" -t count Trace count cache-hops\n"
" -g count Ping mode, \"count\" iterations (0 to loop until interrupted).\n"
- " -I interval Ping interval in seconds (default 1 second).\n",
+ " -I interval Ping interval in seconds (default 1 second).\n"
+ " -H 'string' Extra headers to send. Use quotes to protect new lines.\n",
progname, CACHE_HTTP_PORT);
exit(1);
}
@@ -151,7 +153,9 @@ main(int argc, char *argv[])
int keep_alive = 0;
int opt_noaccept = 0;
int opt_put = 0;
+ int opt_verbose = 0;
char url[BUFSIZ], msg[BUFSIZ], buf[BUFSIZ], hostname[BUFSIZ];
+ char extra_hdrs[BUFSIZ];
const char *method = "GET";
extern char *optarg;
time_t ims = 0;
@@ -163,6 +167,7 @@ main(int argc, char *argv[])
/* set the defaults */
strcpy(hostname, "localhost");
+ extra_hdrs[0] = '\0';
port = CACHE_HTTP_PORT;
to_stdout = 1;
reload = 0;
@@ -176,7 +181,7 @@ main(int argc, char *argv[])
strcpy(url, argv[argc - 1]);
if (url[0] == '-')
usage(argv[0]);
- while ((c = getopt(argc, argv, "ah:P:i:km:p:rst:g:p:I:?")) != -1)
+ while ((c = getopt(argc, argv, "ah:P:i:km:p:rsvt:g:p:I:H:?")) != -1)
switch (c) {
case 'a':
opt_noaccept = 1;
@@ -221,6 +226,15 @@ main(int argc, char *argv[])
if ((ping_int = atoi(optarg) * 1000) <= 0)
usage(argv[0]);
break;
+ case 'H':
+ if (strlen(optarg)) {
+ strncpy(extra_hdrs, optarg, sizeof(extra_hdrs));
+ }
+ break;
+ case 'v':
+ /* undocumented: may increase verb-level by giving more -v's */
+ opt_verbose++;
+ break;
case '?': /* usage */
default:
usage(argv[0]);
@@ -272,9 +286,13 @@ main(int argc, char *argv[])
snprintf(buf, BUFSIZ, "Connection: Keep-Alive\r\n");
strcat(msg, buf);
}
+ strcat(msg, extra_hdrs);
snprintf(buf, BUFSIZ, "\r\n");
strcat(msg, buf);
+ if (opt_verbose)
+ fprintf(stderr, "headers: '%s'\n", msg);
+
if (ping) {
#if HAVE_SIGACTION
struct sigaction sa, osa;
diff --git a/src/enums.h b/src/enums.h
index 88128f3012..ccac5f7ff6 100644
--- a/src/enums.h
+++ b/src/enums.h
@@ -44,6 +44,7 @@ typedef enum {
ERR_URN_RESOLVE,
ERR_ACCESS_DENIED,
ERR_CACHE_ACCESS_DENIED,
+ ERR_CACHE_MGR_ACCESS_DENIED,
ERR_MAX
} err_type;
diff --git a/src/ftp.cc b/src/ftp.cc
index bac074a529..56b63ffaa0 100644
--- a/src/ftp.cc
+++ b/src/ftp.cc
@@ -1,6 +1,6 @@
/*
- * $Id: ftp.cc,v 1.195 1998/02/21 00:56:55 rousskov Exp $
+ * $Id: ftp.cc,v 1.196 1998/02/22 07:45:19 rousskov Exp $
*
* DEBUG: section 9 File Transfer Protocol (FTP)
* AUTHOR: Harvest Derived
@@ -135,7 +135,9 @@ static PF ftpStateFree;
static PF ftpTimeout;
static PF ftpReadControlReply;
static CWCB ftpWriteCommandCallback;
+#if 0
static char *ftpGetBasicAuth(const char *);
+#endif
static void ftpLoginParser(const char *, FtpStateData *);
static wordlist *ftpParseControlReply(char *buf, size_t len, int *code);
static void ftpAppendSuccessHeader(FtpStateData * ftpState);
@@ -814,6 +816,7 @@ ftpDataRead(int fd, void *data)
}
}
+#if 0 /* moved to mime.c because cachemgr needs it too */
static char *
ftpGetBasicAuth(const char *req_hdr)
{
@@ -831,6 +834,7 @@ ftpGetBasicAuth(const char *req_hdr)
return NULL;
return base64_decode(t);
}
+#endif
/*
* ftpCheckAuth
@@ -842,7 +846,7 @@ static int
ftpCheckAuth(FtpStateData * ftpState, char *req_hdr)
{
char *orig_user;
- char *auth;
+ const char *auth;
ftpLoginParser(ftpState->request->login, ftpState);
if (ftpState->user[0] && ftpState->password[0])
return 1; /* name and passwd both in URL */
@@ -851,7 +855,7 @@ ftpCheckAuth(FtpStateData * ftpState, char *req_hdr)
if (ftpState->password[0])
return 1; /* passwd with no name? */
/* URL has name, but no passwd */
- if ((auth = ftpGetBasicAuth(req_hdr)) == NULL)
+ if (!(auth = mime_get_auth(req_hdr, "Basic", NULL)))
return 0; /* need auth header */
orig_user = xstrdup(ftpState->user);
ftpLoginParser(auth, ftpState);
@@ -950,6 +954,7 @@ ftpStart(request_t * request, StoreEntry * entry)
storeAppend(entry, response, strlen(response));
httpParseReplyHeaders(response, entry->mem_obj->reply);
#else
+ /* create reply */
{
HttpReply *reply = entry->mem_obj->reply;
assert(reply);
@@ -2063,18 +2068,17 @@ ftpAuthRequired(const request_t * request, const char *realm)
#endif
static void
-ftpAuthRequired(HttpReply *reply, request_t *request, const char *realm)
+ftpAuthRequired(HttpReply *old_reply, request_t *request, const char *realm)
{
ErrorState *err = errorCon(ERR_ACCESS_DENIED, HTTP_UNAUTHORIZED);
HttpReply *rep;
err->request = requestLink(request);
rep = errorBuildReply(err);
- /* add Authenticate header */
- httpHeaderSetStr(&rep->hdr, HDR_WWW_AUTHENTICATE, realm);
errorStateFree(err);
- /* substitute, should be OK because we clean it @?@ */
- httpReplyClean(reply);
- *reply = *rep; /* @?@ warning is generated due to hdr_sz being constant */
+ /* add Authenticate header */
+ httpHeaderSetAuth(&rep->hdr, "Basic", realm);
+ /* move new reply to the old one */
+ httpReplyAbsorb(old_reply, rep);
}
char *
diff --git a/src/mime.cc b/src/mime.cc
index 6079ff804d..42339891b9 100644
--- a/src/mime.cc
+++ b/src/mime.cc
@@ -1,6 +1,6 @@
/*
- * $Id: mime.cc,v 1.50 1998/02/21 00:56:59 rousskov Exp $
+ * $Id: mime.cc,v 1.51 1998/02/22 07:45:20 rousskov Exp $
*
* DEBUG: section 25 MIME Parsing
* AUTHOR: Harvest Derived
@@ -236,6 +236,26 @@ mk_mime_hdr(char *result, const char *type, int size, time_t ttl, time_t lmt)
}
+const char *
+mime_get_auth(const char *hdr, const char *auth_scheme, const char **auth_field)
+{
+ char *auth_hdr;
+ char *t;
+ if (auth_field) *auth_field = NULL;
+ if (hdr == NULL)
+ return NULL;
+ if ((auth_hdr = mime_get_header(hdr, "Authorization")) == NULL)
+ return NULL;
+ if (auth_field) *auth_field = auth_hdr;
+ if ((t = strtok(auth_hdr, " \t")) == NULL)
+ return NULL;
+ if (strcasecmp(t, auth_scheme) != 0)
+ return NULL;
+ if ((t = strtok(NULL, " \t")) == NULL)
+ return NULL;
+ return base64_decode(t);
+}
+
char *
mimeGetIcon(const char *fn)
{
diff --git a/src/protos.h b/src/protos.h
index e29ad56e44..5d25c8b762 100644
--- a/src/protos.h
+++ b/src/protos.h
@@ -293,6 +293,8 @@ extern int ipcacheUnregister(const char *name, void *data);
extern char *mime_get_header(const char *mime, const char *header);
extern char *mime_headers_end(const char *mime);
extern int mk_mime_hdr(char *result, const char *type, int size, time_t ttl, time_t lmt);
+extern const char *mime_get_auth(const char *hdr, const char *auth_scheme, const char **auth_field);
+
extern void mimeInit(char *filename);
extern char *mimeGetContentEncoding(const char *fn);
extern char *mimeGetContentType(const char *fn);
@@ -340,7 +342,7 @@ extern int netdbHostHops(const char *host);
extern int netdbHostRtt(const char *host);
extern void netdbUpdatePeer(request_t *, peer * e, int rtt, int hops);
-extern void cachemgrStart(int fd, StoreEntry *);
+extern void cachemgrStart(int fd, request_t *request, StoreEntry * entry);
extern void cachemgrRegister(const char *, const char *, OBJH *, int);
extern void cachemgrInit(void);