static int http_da_parse(struct soap *soap) { struct http_da_data *data = (struct http_da_data*)soap_lookup_plugin(soap, http_da_id); if (!data) return SOAP_PLUGIN_ERROR; data->qop = NULL; /* HTTP GET w/o body with qop=auth-int still requires a digest */ if (soap_smd_init(soap, &data->smd_data, SOAP_SMD_DGST_MD5, NULL, 0) || soap_smd_final(soap, &data->smd_data, data->digest, NULL)) return soap->error; if ((soap->error = data->fparse(soap))) return soap->error; if (data->qop && !soap_tag_cmp(data->qop, "auth-int")) { if (soap->fpreparerecv != http_da_preparerecv) { data->fpreparerecv = soap->fpreparerecv; soap->fpreparerecv = http_da_preparerecv; } if (soap->fpreparefinalrecv != http_da_preparefinalrecv) { data->fpreparefinalrecv = soap->fpreparefinalrecv; soap->fpreparefinalrecv = http_da_preparefinalrecv; } if (soap_smd_init(soap, &data->smd_data, SOAP_SMD_DGST_MD5, NULL, 0)) return soap->error; } return SOAP_OK; }
static void http_da_delete(struct soap *soap, struct soap_plugin *p) { if (((struct http_da_data*)p->data)->smd_data.ctx) soap_smd_final(soap, &((struct http_da_data*)p->data)->smd_data, NULL, NULL); if (p->data) SOAP_FREE(soap, p->data); }
static int http_da_calc_HA1(struct soap *soap, struct soap_smd_data *smd_data, const char *alg, const char *userid, const char *realm, const char *passwd, const char *nonce, const char *cnonce, char HA1hex[65]) { int smd_alg = SOAP_SMD_DGST_MD5; size_t smd_len = 16; char HA1[32]; if (alg && !soap_tag_cmp(alg, "SHA-256*")) { smd_alg = SOAP_SMD_DGST_SHA256; smd_len = 32; } if (soap_smd_init(soap, smd_data, smd_alg, NULL, 0) || soap_smd_update(soap, smd_data, userid, strlen(userid)) || soap_smd_update(soap, smd_data, ":", 1) || soap_smd_update(soap, smd_data, realm, strlen(realm)) || soap_smd_update(soap, smd_data, ":", 1) || soap_smd_update(soap, smd_data, passwd, strlen(passwd)) || soap_smd_final(soap, smd_data, HA1, NULL)) return soap->error; if (alg && !soap_tag_cmp(alg, "*-sess")) { if (soap_smd_init(soap, smd_data, smd_alg, NULL, 0) || soap_smd_update(soap, smd_data, HA1, smd_len)) return soap->error; if (nonce) { if (soap_smd_update(soap, smd_data, ":", 1) || soap_smd_update(soap, smd_data, nonce, strlen(nonce))) return soap->error; } if (soap_smd_update(soap, smd_data, ":", 1) || soap_smd_update(soap, smd_data, cnonce, strlen(cnonce)) || soap_smd_final(soap, smd_data, HA1, NULL)) return soap->error; } (void)soap_s2hex(soap, (unsigned char*)HA1, HA1hex, smd_len); return SOAP_OK; };
/* Most of this function is taken from the function 'soap_wsse_add_UsernameTokenDigest()' defined in wsseapi.cpp // // Used to alter the soap request for an offset time (for authorization purposes - replay attack protection) */ int OnvifClientDevice::LocalAddUsernameTokenDigest(struct soap *soapOff,double cam_pc_offset) { string strUrl; string strUser; string strPass; if (this->GetUserPasswd(strUser, strPass) == false || this->GetUrl(strUrl) == false) { return SOAP_ERR; } /* start soap_wsse_add_UsernameTokenDigest; Taken from wsseapi.cpp*/ /* All of this is taken from the function soap_wsse_add_UsernameTokenDigest() defined in wsseapi.cpp */ _wsse__Security *security = soap_wsse_add_Security(soapOff); time_t now = time(NULL); now -= (time_t) cam_pc_offset; //offset so digest comes out correctly (synced times between cam and pc); const char *created = soap_dateTime2s(soapOff, now); char HA[SOAP_SMD_SHA1_SIZE], HABase64[29]; char nonce[20], *nonceBase64; /*start calc_nonce(soapOff, nonce); Taken from wsseapi.cpp */ time_t r = time(NULL); cout << "now time: " << r << endl; r -= (time_t) cam_pc_offset; //offset so digest comes out correctly (synced times between cam and pc); cout << "now time minus offset: " << r << endl; memcpy(nonce, &r, 4); for (int i = 4; i < 20; i += 4) { r = soap_random; memcpy(nonce + i, &r, 4); } /*end calc_nonce(soapOff, nonce); */ nonceBase64 = soap_s2base64(soapOff, (unsigned char*)nonce, NULL, 20); /* start calc_digest(soapOff, created, nonce, 20, strPass, HA); Taken from wsseapi.cpp */ struct soap_smd_data context; /* use smdevp engine */ soap_smd_init(soapOff, &context, SOAP_SMD_DGST_SHA1, NULL, 0); soap_smd_update(soapOff, &context, nonce, 20); soap_smd_update(soapOff, &context, created, strlen(created)); soap_smd_update(soapOff, &context, strPass.c_str(), strlen(strPass.c_str())); soap_smd_final(soapOff, &context, HA, NULL); /* end calc_digest(soapOff, created, nonce, 20, strPass, HA); */ soap_s2base64(soapOff, (unsigned char*)HA, HABase64, SOAP_SMD_SHA1_SIZE); /* populate the UsernameToken with digest */ soap_wsse_add_UsernameTokenText(soapOff, "Id", strUser.c_str(), HABase64); /* populate the remainder of the password, nonce, and created */ security->UsernameToken->Password->Type = (char*)wsse_PasswordDigestURI; security->UsernameToken->Nonce = nonceBase64; security->UsernameToken->wsu__Created = soap_strdup(soapOff, created); /* end soap_wsse_add_UsernameTokenDigest */ return SOAP_OK; }
static int http_da_preparefinalrecv(struct soap *soap) { struct http_da_data *data = (struct http_da_data*)soap_lookup_plugin(soap, http_da_id); if (!data) return SOAP_PLUGIN_ERROR; if (soap_smd_final(soap, &data->smd_data, data->digest, NULL)) return soap->error; soap->fpreparerecv = data->fpreparerecv; soap->fpreparefinalrecv = data->fpreparefinalrecv; if (soap->fpreparefinalrecv) return soap->fpreparefinalrecv(soap); return SOAP_OK; }
/** @fn int soap_smd_end(struct soap *soap, char *buf, int *len) @brief Completes a digest or signature computation. @param soap context @param[in] buf contains signature for verification (when using a SOAP_SMD_VRFY algorithm) @param[out] buf is populated with the digest or signature @param[in] len points to length of signature to verify (when using a SOAP_SMD_VRFY algorithm) @param[out] len points to length of stored digest or signature (when not NULL) @return SOAP_OK, SOAP_USER_ERROR, or SOAP_SSL_ERROR */ int soap_smd_end(struct soap *soap, char *buf, int *len) { struct soap_smd_data *data; int err; data = (struct soap_smd_data*)soap->user; if (!data) return SOAP_USER_ERROR; /* finalize the digest/signature computation and store data in buf + len */ /* for signature verification, buf + len contain the signature */ err = soap_smd_final(soap, data, buf, len); /* restore the callbacks */ soap->fsend = data->fsend; soap->frecv = data->frecv; /* restore the mode flag */ soap->mode = data->mode; /* restore the 'user' data */ soap->user = data->user; /* free data */ SOAP_FREE(soap, data); /* return SOAP_OK or error */ return err; }
static int http_da_post_header(struct soap *soap, const char *key, const char *val) { struct http_da_data *data = (struct http_da_data*)soap_lookup_plugin(soap, http_da_id); if (!data) return SOAP_PLUGIN_ERROR; /* client's HTTP Authorization request */ if (key && (!strcmp(key, "Authorization") || !strcmp(key, "Proxy-Authorization"))) { char HA1hex[65], entityHAhex[65], response[65], responseHA[32]; char cnonce[HTTP_DA_NONCELEN]; char ncount[9]; const char *qop, *method; const char *userid = (*key == 'A' ? soap->userid : soap->proxy_userid); const char *passwd = (*key == 'A' ? soap->passwd : soap->proxy_passwd); size_t smd_len = 16; if (data->alg && !soap_tag_cmp(data->alg, "SHA-256*")) smd_len = 32; if (soap_smd_final(soap, &data->smd_data, data->digest, NULL)) return soap->error; if (!userid || !passwd || !soap->authrealm || !data->nonce) { #ifdef SOAP_DEBUG fprintf(stderr, "Debug message: authentication header construction failed, missing some of the authentication data!\n"); #endif return SOAP_OK; } http_da_calc_nonce(soap, cnonce); if (http_da_calc_HA1(soap, &data->smd_data, data->alg, userid, soap->authrealm, passwd, data->nonce, cnonce, HA1hex)) return soap->error; if (soap->status != SOAP_GET && soap->status != SOAP_CONNECT && data->qop && !soap_tag_cmp(data->qop, "*auth-int*")) { qop = "auth-int"; (void)soap_s2hex(soap, (unsigned char*)data->digest, entityHAhex, smd_len); } else if (data->qop) qop = "auth"; else qop = NULL; if (soap->status == SOAP_GET) method = "GET"; else if (soap->status == SOAP_CONNECT) method = "CONNECT"; else method = "POST"; (SOAP_SNPRINTF(ncount, sizeof(ncount), 8), "%8.8lx", data->nc++); if (http_da_calc_response(soap, &data->smd_data, data->alg, HA1hex, data->nonce, ncount, cnonce, qop, method, soap->path, entityHAhex, response, responseHA)) return soap->error; (SOAP_SNPRINTF(soap->tmpbuf, sizeof(soap->tmpbuf), strlen(soap->authrealm) + strlen(userid) + strlen(data->nonce) + strlen(soap->path) + strlen(ncount) + strlen(cnonce) + strlen(response) + 75), "Digest algorithm=%s, realm=\"%s\", username=\"%s\", nonce=\"%s\", uri=\"%s\", nc=%s, cnonce=\"%s\", response=\"%s\"", data->alg ? data->alg : "MD5", soap->authrealm, userid, data->nonce, soap->path, ncount, cnonce, response); if (data->opaque) { size_t l = strlen(soap->tmpbuf); (SOAP_SNPRINTF(soap->tmpbuf + l, sizeof(soap->tmpbuf) - l, strlen(data->opaque) + 11), ", opaque=\"%s\"", data->opaque); } if (qop) { size_t l = strlen(soap->tmpbuf); (SOAP_SNPRINTF(soap->tmpbuf + l, sizeof(soap->tmpbuf) - l, strlen(qop) + 8), ", qop=\"%s\"", qop); } return data->fposthdr(soap, key, soap->tmpbuf); } /* server's HTTP Authorization challenge/response */ if (key && (!strcmp(key, "WWW-Authenticate") || !strcmp(key, "Proxy-Authenticate"))) { static const char *algos[] = { "MD5", "MD5-sess", "SHA-256", "SHA-256-sess", "SHA-512-256", "SHA-512-256-sess" }; const char *alg = algos[data->option]; char nonce[HTTP_DA_NONCELEN]; char opaque[HTTP_DA_OPAQUELEN]; http_da_calc_nonce(soap, nonce); http_da_calc_opaque(soap, opaque); http_da_session_start(soap->authrealm, nonce, opaque); if (data->option > 0) { (SOAP_SNPRINTF(soap->tmpbuf, sizeof(soap->tmpbuf), strlen(soap->authrealm) + strlen(nonce) + strlen(opaque) + 59), "Digest algorithm=%s, realm=\"%s\", qop=\"auth,auth-int\", nonce=\"%s\", opaque=\"%s\"", alg, soap->authrealm, nonce, opaque); if (data->fposthdr(soap, key, soap->tmpbuf)) return soap->error; } (SOAP_SNPRINTF(soap->tmpbuf, sizeof(soap->tmpbuf), strlen(soap->authrealm) + strlen(nonce) + strlen(opaque) + 59), "Digest algorithm=MD5, realm=\"%s\", qop=\"auth,auth-int\", nonce=\"%s\", opaque=\"%s\"", soap->authrealm, nonce, opaque); return data->fposthdr(soap, key, soap->tmpbuf); } return data->fposthdr(soap, key, val); }
static int http_da_calc_response(struct soap *soap, struct soap_smd_data *smd_data, const char *alg, char HA1hex[65], const char *nonce, const char *ncount, const char *cnonce, const char *qop, const char *method, const char *uri, char entityHAhex[65], char response[65], char responseHA[32]) { int smd_alg = SOAP_SMD_DGST_MD5; size_t smd_len = 16; char HA2[32], HA2hex[65]; if (alg && !soap_tag_cmp(alg, "SHA-256*")) { smd_alg = SOAP_SMD_DGST_SHA256; smd_len = 32; } if (soap_smd_init(soap, smd_data, smd_alg, NULL, 0) || soap_smd_update(soap, smd_data, method, strlen(method)) || soap_smd_update(soap, smd_data, ":", 1) || soap_smd_update(soap, smd_data, uri, strlen(uri))) return soap->error; if (qop && !soap_tag_cmp(qop, "auth-int")) { if (soap_smd_update(soap, smd_data, ":", 1) || soap_smd_update(soap, smd_data, entityHAhex, 2*smd_len)) return soap->error; } if (soap_smd_final(soap, smd_data, HA2, NULL)) return soap->error; (void)soap_s2hex(soap, (unsigned char*)HA2, HA2hex, smd_len); if (soap_smd_init(soap, smd_data, smd_alg, NULL, 0) || soap_smd_update(soap, smd_data, HA1hex, 2*smd_len)) return soap->error; if (nonce) { if (soap_smd_update(soap, smd_data, ":", 1) || soap_smd_update(soap, smd_data, nonce, strlen(nonce))) return soap->error; } if (qop && *qop) { if (soap_smd_update(soap, smd_data, ":", 1) || soap_smd_update(soap, smd_data, ncount, strlen(ncount)) || soap_smd_update(soap, smd_data, ":", 1) || soap_smd_update(soap, smd_data, cnonce, strlen(cnonce)) || soap_smd_update(soap, smd_data, ":", 1) || soap_smd_update(soap, smd_data, qop, strlen(qop))) return soap->error; } if (soap_smd_update(soap, smd_data, ":", 1) || soap_smd_update(soap, smd_data, HA2hex, 2*smd_len) || soap_smd_final(soap, smd_data, responseHA, NULL)) return soap->error; (void)soap_s2hex(soap, (unsigned char*)responseHA, response, smd_len); return SOAP_OK; }