bin/121074: Add RFC2617 digest authentication to fetch(3)
Yen-Ming Lee
leeym at FreeBSD.org
Mon Feb 25 10:00:02 UTC 2008
>Number: 121074
>Category: bin
>Synopsis: Add RFC2617 digest authentication to fetch(3)
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: change-request
>Submitter-Id: current-users
>Arrival-Date: Mon Feb 25 10:00:01 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator: Yen-Ming Lee
>Release: FreeBSD 6.2-RELEASE i386
>Organization:
>Environment:
System: FreeBSD db1.leeym.com 6.2-RELEASE FreeBSD 6.2-RELEASE #0: Fri Jan 12 10:40:27 UTC 2007 root at dessler.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC i386
>Description:
Add RFC2617 digest authentication.
qop=auth-int and non-MD5 algorithm are not implemented yet.
http://www.freebsd.org/projects/ideas/#p-libfetchauth
>How-To-Repeat:
>Fix:
--- libfetchauth.diff begins here ---
Index: Makefile
===================================================================
RCS file: /home/ncvs/src/lib/libfetch/Makefile,v
retrieving revision 1.51
diff -u -r1.51 Makefile
--- Makefile 19 Dec 2007 05:10:07 -0000 1.51
+++ Makefile 25 Feb 2008 09:49:50 -0000
@@ -21,6 +21,7 @@
.endif
CFLAGS+= -DFTP_COMBINE_CWDS
+LDADD+= -lmd
CSTD?= c99
WARNS?= 2
Index: http.c
===================================================================
RCS file: /home/ncvs/src/lib/libfetch/http.c,v
retrieving revision 1.84
diff -u -r1.84 http.c
--- http.c 8 Feb 2008 09:48:48 -0000 1.84
+++ http.c 25 Feb 2008 09:49:50 -0000
@@ -75,6 +75,7 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <md5.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
@@ -127,6 +128,127 @@
};
/*
+ * Authentication scheme types
+ */
+typedef enum {
+ auth_unknown = -1,
+ auth_basic = 0,
+ auth_digest = 1,
+} auth_scheme_t;
+
+/*
+ * Information needed to construct request_digest
+ */
+struct httpdigest
+{
+ /* RFC 2617: 3.2.1 digest-challenge */
+ char *realm;
+ char *domain;
+ char *nonce;
+ char *opaque;
+ char *stale;
+ char *algorithm;
+ char *qop;
+ /* RFC 2617: 3.2.2.3 A2 */
+ char *method;
+ char *uri;
+};
+
+/*
+ * Free a digest
+ */
+void
+http_free_digest(struct httpdigest *d)
+{
+ if (!d)
+ return;
+ if (d->realm)
+ free(d->realm);
+ if (d->domain)
+ free(d->domain);
+ if (d->nonce)
+ free(d->nonce);
+ if (d->opaque)
+ free(d->opaque);
+ if (d->stale)
+ free(d->stale);
+ if (d->algorithm)
+ free(d->algorithm);
+ if (d->qop)
+ free(d->qop);
+ if (d->method)
+ free(d->method);
+ if (d->uri)
+ free(d->uri);
+ free(d);
+}
+
+static char *
+http_parse_value(const char *hdr, const char *key)
+{
+ char *begin, *_key, *end, *value, *sep = ",";
+ int len;
+ len = strlen(key);
+ if ((_key = malloc(len+1)) == NULL)
+ return NULL;
+ sprintf(_key, "%s=", key);
+ begin = strcasestr(hdr, _key);
+ if (!begin)
+ goto ouch;
+ begin += len + 1;
+ if (*begin == '"') {
+ begin++;
+ sep = "\"";
+ }
+ end = strstr(begin, sep);
+ if (!end)
+ goto ouch;
+ len = end - begin + 1;
+ value = malloc(len);
+ strlcpy(value, begin, len);
+ return value;
+
+ouch:
+ free(_key);
+ return NULL;
+}
+
+/*
+ * Construct a digest based on WWW-Authenticate header
+ */
+auth_scheme_t
+http_parse_authenticate(const char *p, const char *method, const char *uri, struct httpdigest *d)
+{
+ /* Only handle "digest" authentication scheme */
+ if (strncasecmp(p, "Basic", 5) == 0) {
+ return auth_basic;
+ } else if (strncasecmp(p, "Digest", 6)) {
+ return auth_unknown;
+ }
+
+ d->realm = http_parse_value(p, "realm");
+ d->domain = http_parse_value(p, "domain");
+ d->nonce = http_parse_value(p, "nonce");
+ d->opaque = http_parse_value(p, "opaque");
+ d->stale = http_parse_value(p, "stale");
+ d->algorithm = http_parse_value(p, "algorithm");
+ d->qop = http_parse_value(p, "qop");
+ d->method = strdup(method);
+ d->uri = strdup(uri);
+
+ if (!d->realm || !d->nonce || !d->method || !d->uri) {
+ http_seterr(HTTP_PROTOCOL_ERROR);
+ goto ouch;
+ }
+
+ return auth_digest;
+
+ouch:
+ http_free_digest(d);
+ return auth_unknown;
+}
+
+/*
* Get next chunk header
*/
static int
@@ -617,6 +739,18 @@
}
/*
+ * MD5 encoding
+ */
+static char *
+http_md5(const char *src, char *dst)
+{
+ MD5_CTX Md5Ctx;
+ MD5Init(&Md5Ctx);
+ MD5Update(&Md5Ctx, src, strlen(src));
+ return MD5End(&Md5Ctx, dst);
+}
+
+/*
* Encode username and password
*/
static int
@@ -639,10 +773,95 @@
}
/*
+ * RFC 2617: Digest Access Authentication
+ */
+static int
+http_digest_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd, struct httpdigest *d)
+{
+ char buf[8192];
+ char HA1[33], HA2[33], request_digest[33];
+ char cnonce[9];
+ int r;
+ static int nc = 0;
+
+ DEBUG(fprintf(stderr, "usr: [%s]\n", usr));
+ DEBUG(fprintf(stderr, "pwd: [%s]\n", pwd));
+
+ if (!d || !d->realm)
+ return(-1);
+ ++nc;
+ sprintf(cnonce, "%08X", time(NULL));
+
+ /* RFC 2617: 3.2.2.2 A1 */
+ if (!d->algorithm || strcasecmp(d->algorithm, "MD5") == 0) {
+ sprintf(buf, "%s:%s:%s", usr, d->realm, pwd);
+ } else if (strcasecmp(d->algorithm, "MD5-sess") == 0) {
+ sprintf(buf, "%s:%s:%s", usr, d->realm, pwd);
+ http_md5(buf, HA1);
+ strcpy(buf, HA1);
+ strcat(buf, ":");
+ strcat(buf, d->nonce);
+ strcat(buf, ":");
+ strcat(buf, cnonce);
+ } else {
+ warnx("http_digest_auth(): non-MD5 algorithm not implemented");
+ return (-1);
+ }
+ http_md5(buf, HA1);
+
+ /* RFC 2617: 3.2.2.3 A2 */
+ if (!d->qop || strcasecmp(d->qop, "auth") == 0) {
+ sprintf(buf, "%s:%s", d->method, d->uri);
+ http_md5(buf, HA2);
+ } else if (strcasecmp(d->qop, "auth-int") == 0) {
+ warnx("http_digest_auth(): qop=auth-int not implemented");
+ return (-1);
+ }
+ /* RFC 2617: 3.2.2.1 Request-Digest */
+ if (!d->qop) {
+ sprintf(buf, "%s:%s:%s", HA1, d->nonce, HA2);
+ http_md5(buf, request_digest);
+ } else if (strcasecmp(d->qop, "auth") == 0 || strcasecmp(d->qop, "auth-int") == 0) {
+ sprintf(buf, "%s:%s:%08d:%s:%s:%s", HA1, d->nonce, nc, cnonce, d->qop, HA2);
+ http_md5(buf, request_digest);
+ }
+ if (d->qop) {
+ sprintf(buf,
+ "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", qop=\"%s\", nc=\"%08d\", cnonce=\"%s\", response=\"%s\"",
+ usr, d->realm, d->nonce, d->uri, d->qop, nc, cnonce, request_digest);
+ } else {
+ sprintf(buf,
+ "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", qop=\"%s\", response=\"%s\"",
+ usr, d->realm, d->nonce, d->uri, d->qop, request_digest);
+ }
+ if (d->opaque) {
+ strcat(buf, ", opaque=\"");
+ strcat(buf, d->opaque);
+ strcat(buf, "\"");
+ }
+ r = http_cmd(conn, "%s: Digest %s", hdr, buf);
+ return (r);
+}
+
+/*
+ * Send an authorization header based on authentication scheme
+ */
+static int
+http_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd, auth_scheme_t auth, struct httpdigest *d)
+{
+ if (auth == auth_basic)
+ return http_basic_auth(conn, hdr, usr, pwd);
+ else if (auth == auth_digest)
+ return http_digest_auth(conn, hdr, usr, pwd, d);
+ else
+ return (-1);
+}
+
+/*
* Send an authorization header
*/
static int
-http_authorize(conn_t *conn, const char *hdr, const char *p)
+http_authorize(conn_t *conn, const char *hdr, const char *p, struct httpdigest *digest)
{
/* basic authorization */
if (strncasecmp(p, "basic:", 6) == 0) {
@@ -663,6 +882,26 @@
free(str);
return (r);
}
+ /* digest authorization */
+ else if (strncasecmp(p, "digest:", 7) == 0) {
+ char *user, *pwd, *str;
+ int r;
+ if (!digest)
+ return (-1);
+ /* skip realm */
+ for (p += 7; *p && *p != ':'; ++p)
+ /* nothing */ ;
+ if (!*p || strchr(++p, ':') == NULL)
+ return (-1);
+ if ((str = strdup(p)) == NULL)
+ return (-1); /* XXX */
+ user = str;
+ pwd = strchr(user, ':');
+ *pwd++ = '\0';
+ r = http_digest_auth(conn, hdr, user, pwd, digest);
+ free(str);
+ return (r);
+ }
return (-1);
}
@@ -807,6 +1046,12 @@
FILE *f;
hdr_t h;
char hbuf[MAXHOSTNAMELEN + 7], *host;
+ auth_scheme_t auth;
+ struct httpdigest *digest = calloc(1, sizeof(struct httpdigest));
+ if (digest == NULL) {
+ DEBUG(fprintf(stderr, "failed to allocate httpdigest\n"));
+ goto ouch;
+ }
direct = CHECK_FLAG('d');
noredirect = CHECK_FLAG('A');
@@ -888,23 +1133,29 @@
http_basic_auth(conn, "Proxy-Authorization",
purl->user, purl->pwd);
else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0')
- http_authorize(conn, "Proxy-Authorization", p);
+ http_authorize(conn, "Proxy-Authorization", p, digest);
}
/* server authorization */
if (need_auth || *url->user || *url->pwd) {
- if (*url->user || *url->pwd)
- http_basic_auth(conn, "Authorization", url->user, url->pwd);
- else if ((p = getenv("HTTP_AUTH")) != NULL && *p != '\0')
- http_authorize(conn, "Authorization", p);
+ if (*url->user || *url->pwd) {
+ http_auth(conn, "Authorization", url->user, url->pwd, auth, digest);
+ }
+ else if ((p = getenv("HTTP_AUTH")) != NULL && *p != '\0') {
+ http_authorize(conn, "Authorization", p, digest);
+ }
else if (fetchAuthMethod && fetchAuthMethod(url) == 0) {
- http_basic_auth(conn, "Authorization", url->user, url->pwd);
+ http_auth(conn, "Authorization", url->user, url->pwd, auth, digest);
} else {
http_seterr(HTTP_NEED_AUTH);
goto ouch;
}
}
+ if (purl || need_auth || *url->user || *url->pwd) {
+ http_free_digest(digest);
+ }
+
/* other headers */
if ((p = getenv("HTTP_REFERER")) != NULL && *p != '\0') {
if (strcasecmp(p, "auto") == 0)
@@ -1038,9 +1289,9 @@
chunked = (strcasecmp(p, "chunked") == 0);
break;
case hdr_www_authenticate:
+ auth = http_parse_authenticate(p, op, url->doc, digest);
if (conn->err != HTTP_NEED_AUTH)
break;
- /* if we were smarter, we'd check the method and realm */
break;
case hdr_end:
/* fall through */
--- libfetchauth.diff ends here ---
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list