/* Get a password digest using the MD5 algorithm -- See RFC 2617 to understand this code. */ static char *calcDigest(HttpConn *conn, DigestData *dp, cchar *username) { HttpAuth *auth; char *digestBuf, *ha1, *ha2; auth = conn->rx->route->auth; if (!conn->user) { conn->user = mprLookupKey(auth->userCache, username); } assert(conn->user && conn->user->password); if (conn->user == 0 || conn->user->password == 0) { return 0; } /* Compute HA1. Password is already expected to be in the HA1 format MD5(username:realm:password). */ ha1 = sclone(conn->user->password); /* HA2 */ ha2 = mprGetMD5(sfmt("%s:%s", conn->rx->method, dp->uri)); /* H(HA1:nonce:HA2) */ if (scmp(dp->qop, "auth") == 0) { digestBuf = sfmt("%s:%s:%s:%s:%s:%s", ha1, dp->nonce, dp->nc, dp->cnonce, dp->qop, ha2); } else { digestBuf = sfmt("%s:%s:%s", ha1, dp->nonce, ha2); } return mprGetMD5(digestBuf); }
/* Add the client 'Authorization' header for authenticated requests Must first get a 401 response to get the authData. */ PUBLIC bool httpDigestSetHeaders(HttpConn *conn, cchar *username, cchar *password) { Http *http; HttpTx *tx; DigestData *dp; char *ha1, *ha2, *digest, *cnonce; http = conn->http; tx = conn->tx; if ((dp = conn->authData) == 0) { /* Need to await a failing auth response */ return 0; } cnonce = sfmt("%s:%s:%x", http->secret, dp->realm, (int) http->now); ha1 = mprGetMD5(sfmt("%s:%s:%s", username, dp->realm, password)); ha2 = mprGetMD5(sfmt("%s:%s", tx->method, tx->parsedUri->path)); if (smatch(dp->qop, "auth")) { digest = mprGetMD5(sfmt("%s:%s:%08x:%s:%s:%s", ha1, dp->nonce, dp->nc, cnonce, dp->qop, ha2)); httpAddHeader(conn, "Authorization", "Digest username=\"%s\", realm=\"%s\", domain=\"%s\", " "algorithm=\"MD5\", qop=\"%s\", cnonce=\"%s\", nc=\"%08x\", nonce=\"%s\", opaque=\"%s\", " "stale=\"FALSE\", uri=\"%s\", response=\"%s\"", username, dp->realm, dp->domain, dp->qop, cnonce, dp->nc, dp->nonce, dp->opaque, tx->parsedUri->path, digest); } else { digest = mprGetMD5(sfmt("%s:%s:%s", ha1, dp->nonce, ha2)); httpAddHeader(conn, "Authorization", "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", " "uri=\"%s\", response=\"%s\"", username, dp->realm, dp->nonce, tx->parsedUri->path, digest); } return 1; }
/* See if there is acceptable cached content for this request. If so, return true. Will setup tx->cacheBuffer as a side-effect if the output should be captured and cached. */ static bool fetchCachedResponse(HttpConn *conn) { HttpTx *tx; MprTime modified, when; cchar *value, *key, *tag; int status, cacheOk, canUseClientCache; tx = conn->tx; /* Transparent caching. Manual caching must manually call httpWriteCached() */ key = makeCacheKey(conn); if ((value = httpGetHeader(conn, "Cache-Control")) != 0 && (scontains(value, "max-age=0") == 0 || scontains(value, "no-cache") == 0)) { mprLog(3, "Client reload. Cache-control header '%s' rejects use of cached content.", value); } else if ((tx->cachedContent = mprReadCache(conn->host->responseCache, key, &modified, 0)) != 0) { /* See if a NotModified response can be served. This is much faster than sending the response. Observe headers: If-None-Match: "ec18d-54-4d706a63" If-Modified-Since: Fri, 04 Mar 2012 04:28:19 GMT Set status to OK when content must be transmitted. */ cacheOk = 1; canUseClientCache = 0; tag = mprGetMD5(key); if ((value = httpGetHeader(conn, "If-None-Match")) != 0) { canUseClientCache = 1; if (scmp(value, tag) != 0) { cacheOk = 0; } } if (cacheOk && (value = httpGetHeader(conn, "If-Modified-Since")) != 0) { canUseClientCache = 1; mprParseTime(&when, value, 0, 0); if (modified > when) { cacheOk = 0; } } status = (canUseClientCache && cacheOk) ? HTTP_CODE_NOT_MODIFIED : HTTP_CODE_OK; mprLog(3, "cacheHandler: Use cached content for %s, status %d", key, status); httpSetStatus(conn, status); httpSetHeader(conn, "Etag", mprGetMD5(key)); httpSetHeader(conn, "Last-Modified", mprFormatUniversalTime(MPR_HTTP_DATE, modified)); return 1; } mprLog(3, "cacheHandler: No cached content for %s", key); return 0; }
ssize httpWriteCached(HttpConn *conn) { MprTime modified; cchar *cacheKey, *data, *content; if (!conn->tx->cache) { return MPR_ERR_CANT_FIND; } cacheKey = makeCacheKey(conn); if ((content = mprReadCache(conn->host->responseCache, cacheKey, &modified, 0)) == 0) { mprLog(3, "No cached data for ", cacheKey); return 0; } mprLog(5, "Used cached ", cacheKey); data = setHeadersFromCache(conn, content); httpSetHeader(conn, "Etag", mprGetMD5(cacheKey)); httpSetHeader(conn, "Last-Modified", mprFormatUniversalTime(MPR_HTTP_DATE, modified)); conn->tx->cacheBuffer = 0; httpWriteString(conn->writeq, data); httpFinalize(conn); return slen(data); }
/* Verify the user password for the "config" store based on the users defined via configuration directives. Password may be NULL only if using auto-login. */ static bool configVerifyUser(HttpConn *conn, cchar *username, cchar *password) { HttpRx *rx; HttpAuth *auth; bool success; char *requiredPassword; rx = conn->rx; auth = rx->route->auth; if (!conn->user && (conn->user = mprLookupKey(auth->userCache, username)) == 0) { httpTrace(conn, "auth.login.error", "error", "msg: 'Unknown user', username:'******'", username); return 0; } if (password) { if (auth->realm == 0 || *auth->realm == '\0') { mprLog("error http auth", 0, "No AuthRealm defined"); } requiredPassword = (rx->passwordDigest) ? rx->passwordDigest : conn->user->password; if (sncmp(requiredPassword, "BF", 2) == 0 && slen(requiredPassword) > 4 && isdigit(requiredPassword[2]) && requiredPassword[3] == ':') { /* Blowifsh */ success = mprCheckPassword(sfmt("%s:%s:%s", username, auth->realm, password), conn->user->password); } else { if (!conn->encoded) { password = mprGetMD5(sfmt("%s:%s:%s", username, auth->realm, password)); conn->encoded = 1; } success = smatch(password, requiredPassword); } if (success) { httpTrace(conn, "auth.login.authenticated", "context", "msg:'User authenticated', username:'******'", username); } else { httpTrace(conn, "auth.login.error", "error", "msg:'Password failed to authenticate', username:'******'", username); } return success; } return 1; }
PUBLIC cchar *nonce() { return mprGetMD5(itos(mprRandom())); }
PUBLIC cchar *md5(cchar *str) { return mprGetMD5(str); }
PUBLIC int espEmail(HttpConn *conn, cchar *to, cchar *from, cchar *subject, MprTime date, cchar *mime, cchar *message, MprList *files) { MprList *lines; MprCmd *cmd; cchar *body, *boundary, *contents, *encoded, *file; char *out, *err; ssize length; int i, next, status; if (!from || !*from) { from = "anonymous"; } if (!subject || !*subject) { subject = "Mail message"; } if (!mime || !*mime) { mime = "text/plain"; } if (!date) { date = mprGetTime(); } boundary = sjoin("esp.mail=", mprGetMD5("BOUNDARY"), NULL); lines = mprCreateList(0, 0); mprAddItem(lines, sfmt("To: %s", to)); mprAddItem(lines, sfmt("From: %s", from)); mprAddItem(lines, sfmt("Date: %s", mprFormatLocalTime(0, date))); mprAddItem(lines, sfmt("Subject: %s", subject)); mprAddItem(lines, "MIME-Version: 1.0"); mprAddItem(lines, sfmt("Content-Type: multipart/mixed; boundary=%s", boundary)); mprAddItem(lines, ""); boundary = sjoin("--", boundary, NULL); mprAddItem(lines, boundary); mprAddItem(lines, sfmt("Content-Type: %s", mime)); mprAddItem(lines, ""); mprAddItem(lines, ""); mprAddItem(lines, message); for (ITERATE_ITEMS(files, file, next)) { mprAddItem(lines, boundary); if ((mime = mprLookupMime(NULL, file)) == 0) { mime = "application/octet-stream"; } mprAddItem(lines, "Content-Transfer-Encoding: base64"); mprAddItem(lines, sfmt("Content-Disposition: inline; filename=\"%s\"", mprGetPathBase(file))); mprAddItem(lines, sfmt("Content-Type: %s; name=\"%s\"", mime, mprGetPathBase(file))); mprAddItem(lines, ""); contents = mprReadPathContents(file, &length); encoded = mprEncode64Block(contents, length); for (i = 0; i < length; i += 76) { mprAddItem(lines, snclone(&encoded[i], i + 76)); } } mprAddItem(lines, sfmt("%s--", boundary)); body = mprListToString(lines, "\n"); httpTraceContent(conn, "esp.email", "context", body, slen(body), 0); cmd = mprCreateCmd(conn->dispatcher); if (mprRunCmd(cmd, "sendmail -t", NULL, body, &out, &err, -1, 0) < 0) { mprDestroyCmd(cmd); return MPR_ERR_CANT_OPEN; } if (mprWaitForCmd(cmd, ME_ESP_EMAIL_TIMEOUT) < 0) { httpTrace(conn, "esp.email.error", "error", "msg=\"Timeout waiting for command to complete\", timeout=%d, command=\"%s\"", ME_ESP_EMAIL_TIMEOUT, cmd->argv[0]); mprDestroyCmd(cmd); return MPR_ERR_CANT_COMPLETE; } if ((status = mprGetCmdExitStatus(cmd)) != 0) { httpTrace(conn, "esp.email.error", "error", "msg=\"Sendmail failed\", status=%d, error=\"%s\"", status, err); mprDestroyCmd(cmd); return MPR_ERR_CANT_WRITE; } mprDestroyCmd(cmd); return 0; }