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
--- /dev/null
+<HTML><HEAD>
+<TITLE>ERROR: Cache Manager Access Denied</TITLE>
+</HEAD>
+<BODY>
+<H1>ERROR</H1>
+<H2>Cache Manager Access Denied</H2>
+<HR>
+<P>
+While trying to retrieve the URL:
+<A HREF="%U">%U</A>
+<P>
+The following error was encountered:
+<UL>
+<LI>
+<STRONG>
+Cache Manager Access Denied.
+</STRONG>
+</UL>
+</P>
+
+<P>Sorry, you are not currently allowed to request:
+<PRE> %U</PRE>
+from this cache manager until you have authenticated yourself.
+</P>
+
+<P> 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 <A HREF="mailto:%w">cache administrator</a> if you have
+difficulties authenticating yourself or, if you <em>are</em> the
+administrator, read Squid documentation on cache manager interface and check
+cache log for more detailed error messages.</P>
/*
- * $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
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)
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;
/*
- * $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
/* 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);
assert(rep);
tmp_debug(here) ("destroying rep: %p\n", rep);
httpReplyClean(rep);
- memFree(MEM_HTTPREPLY, rep);
+ httpReplyDoDestroy(rep);
}
void
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)
/* 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.
/*
- * $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
typedef struct {
StoreEntry *entry;
char *action;
+ char *user_name;
char *passwd;
} cachemgrStateData;
} 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 *);
}
static cachemgrStateData *
-cachemgrParse(const char *url)
+cachemgrParseUrl(const char *url)
{
int t;
LOCAL_ARRAY(char, host, MAX_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:"<none>");
+ 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
*/
return 1;
if (strcmp(pwd, "none") == 0)
return 0;
+ if (!mgr->passwd)
+ return 1;
return strcmp(pwd, mgr->passwd);
}
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);
}
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 : "<unknown>",
+ 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 : "<unknown>", 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;
/*
- * $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
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);
}
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;
/* set the defaults */
strcpy(hostname, "localhost");
+ extra_hdrs[0] = '\0';
port = CACHE_HTTP_PORT;
to_stdout = 1;
reload = 0;
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;
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]);
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;
ERR_URN_RESOLVE,
ERR_ACCESS_DENIED,
ERR_CACHE_ACCESS_DENIED,
+ ERR_CACHE_MGR_ACCESS_DENIED,
ERR_MAX
} err_type;
/*
- * $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
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);
}
}
+#if 0 /* moved to mime.c because cachemgr needs it too */
static char *
ftpGetBasicAuth(const char *req_hdr)
{
return NULL;
return base64_decode(t);
}
+#endif
/*
* ftpCheckAuth
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 */
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);
storeAppend(entry, response, strlen(response));
httpParseReplyHeaders(response, entry->mem_obj->reply);
#else
+ /* create reply */
{
HttpReply *reply = entry->mem_obj->reply;
assert(reply);
#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 *
/*
- * $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
}
+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)
{
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);
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);