1 //----------------------------------------------------------------------------
3 // Copyright (C) Intel Corporation, 2003 - 2005.
5 // File: httpDigest.cpp
7 // Contents: Sample code for a gSOAP plugin to implement HTTP Digest
11 // - MIME, DIME and HTTP chunks (SOAP_IO_CHUNK) are not supported.
12 // - This implementationn will internally buffer the entire outgoing
13 // message before sending
14 // - This implementation will fail if challenge isn't received within
15 // SOAP_BUFLEN bytes read.
16 // - This implementation will fail if challenge or response are larger
17 // than the constants we used.
18 // - This implementation calculates the digest response for each call
19 // and doesn't save information.
20 // - This implementation assumes that the algorithm is MD5 and that
23 // Usage: Add the httpDigest.h and httpDigest.cpp files to your project
25 // In your source, just after calling soap_init(), register this
26 // plugin with soap_register_plugin( soap, http_digest ).
27 // Use soap.userid and soap.passwd for the username and password.
28 // As in gSOAP, username and password have to be provided for each call.
32 // soap_init( &soap );
33 // soap_register_plugin( &soap, http_digest );
34 // soap.userid = "admin";
35 // soap.passwd = "admin";
39 //----------------------------------------------------------------------------
41 #include "httpDigest.h"
46 const char HTTP_DIGEST_ID[12] = "HTTP-Digest"; // plugin identification
48 struct http_digest_data
50 struct soap *soap; // back pointer
52 // send buffer parameters
53 char *sendBuffer; // send buffer
54 size_t sendMsgSize; // total length of the message
55 size_t sendBufferLength; // length of data in buffer
57 // receive buffer parameters
58 char *rcvBuffer; // receive buffer
59 size_t rcvSize; // length of buffer
60 size_t rcvIdx; // currecnt index
61 size_t rcvRead; // amount of data read
69 int (*fopen)(struct soap*, const char*, const char*, int); // open function
70 int (*fsend)(struct soap*, const char*, size_t); // send function
71 size_t (*frecv)(struct soap*, char*, size_t); // receive function
72 int (*fposthdr)(struct soap*, const char*, const char*); // post header function
80 static int http_digest_init(struct soap *soap, struct http_digest_data *data);
81 static int http_digest_copy(struct soap *soap, struct soap_plugin *dst,
82 struct soap_plugin *src);
83 static void http_digest_delete(struct soap *soap, struct soap_plugin *p);
85 static int http_digest_fopen(struct soap *soap, const char *endpoint, const char *host,
87 static int http_digest_fsend(struct soap *soap, const char *buf, size_t size);
88 static size_t http_digest_frecv(struct soap *soap, char *buf, size_t size);
89 static int http_digest_fposthdr(struct soap *soap, const char *key, const char *value);
92 * The entry point for HTTP digest plugin
94 * soap - pointer to the soap runtime environment
95 * p - pointer to the soap plugin
96 * arg - reserved. Should be NULL
98 * Returns SOAP_OK for suceess. Other values for error
100 int http_digest(struct soap *soap, struct soap_plugin *p, void *arg)
102 p->id = HTTP_DIGEST_ID;
103 p->data = (void*)malloc(sizeof(struct http_digest_data));
104 p->fcopy = http_digest_copy;
105 p->fdelete = http_digest_delete;
107 memset(p->data, 0, sizeof(struct http_digest_data));
108 if (http_digest_init(soap, (struct http_digest_data*)p->data)) {
118 * Initializes the http digest data structure.
120 * soap - pointer to the soap runtime environment
121 * data - pointer to the http digest data structure
123 * Returns SOAP_OK for suceess. Other values for error
125 static int http_digest_init(struct soap *soap, struct http_digest_data *data)
128 data->fopen = soap->fopen;
129 soap->fopen = http_digest_fopen;
130 data->fsend = soap->fsend;
131 soap->fsend = http_digest_fsend;
132 data->frecv = soap->frecv;
133 soap->frecv = http_digest_frecv;
134 data->fposthdr = soap->fposthdr;
135 soap->fposthdr = http_digest_fposthdr;
141 * Creates the HTTP digest response
143 * userName - the user name
144 * password - the password
145 * method - the HTTP method ("GET", "POST)
146 * realm - the realm for the authentication
147 * uri - the URI from the HTTP request
148 * nonce - the nonce from the challenge
149 * cnonce - client generated nonce
150 * digestResponse - The result authorization string
151 * length - size of buffer for response
153 * Returns 0 for suceess. -1 for error
155 int CalculateResponse(char *userName, char *password, char *method, char *realm, char *uri,
156 char *nonce, char *cnonce, char *digestResponse, size_t *length)
158 size_t currOffset = 0, segmentLength;
159 static const char *INITIAL_HDR = "Authorization: Digest username=\"";
160 static const char *REALEM_HDR = "\", realm=\"";
161 static const char *ALGO_HDR = "\", qop=\"auth\", algorithm=\"MD5\", uri=\"";
162 static const char *NONCE_HDR = "\", nonce=\"";
163 static const char *NC_HDR = "\", nc=00000002, cnonce=\"";
164 static const char *RSP_HDR = "\", response=\"";
170 //"Authorization: Digest username="
171 segmentLength = strlen(INITIAL_HDR);
172 if (*length < (currOffset + segmentLength)) {
175 memcpy(digestResponse + currOffset, INITIAL_HDR, segmentLength);
176 currOffset += segmentLength;
178 //"Authorization: Digest username="username
179 segmentLength = strlen(userName);
180 if (*length < (currOffset + segmentLength)) {
183 memcpy(digestResponse + currOffset, userName, segmentLength);
184 currOffset += segmentLength;
186 //"Authorization: Digest username="username", realm="
187 segmentLength = strlen(REALEM_HDR);
188 if (*length < (currOffset + segmentLength)) {
191 memcpy(digestResponse + currOffset, REALEM_HDR, segmentLength);
192 currOffset += segmentLength;
194 //"Authorization: Digest username="username", realm="realm
195 segmentLength = strlen(realm);
196 if (*length < (currOffset + segmentLength)) {
199 memcpy(digestResponse + currOffset, realm, segmentLength);
200 currOffset += segmentLength;
202 //"Authorization: Digest username="username", realm="myRealm", qop="auth",
203 //algorithm="MD5", uri="
204 segmentLength = strlen(ALGO_HDR);
205 if (*length < (currOffset + segmentLength)) {
208 memcpy(digestResponse + currOffset, ALGO_HDR, segmentLength);
209 currOffset += segmentLength;
211 //"Authorization: Digest username="username", realm="myRealm", qop="auth",
212 //algorithm="MD5", uri="/....Service
213 segmentLength = strlen(uri);
214 if (*length < (currOffset + segmentLength)) {
217 memcpy(digestResponse + currOffset, uri, segmentLength);
218 currOffset+= segmentLength;
220 //"Authorization: Digest username="username", realm="myRealm", qop="auth",
221 //algorithm="MD5", uri="/....Service", nonce="
222 segmentLength = strlen(NONCE_HDR);
223 if (*length < (currOffset + segmentLength)) {
226 memcpy(digestResponse + currOffset, NONCE_HDR, segmentLength);
227 currOffset += segmentLength;
229 //"Authorization: Digest username="username", realm="myRealm", qop="auth",
230 //algorithm="MD5", uri="/....Service", nonce="7a5c...
231 segmentLength = strlen(nonce);
232 if (*length < (currOffset + segmentLength)) {
235 memcpy(digestResponse + currOffset, nonce, segmentLength);
236 currOffset += segmentLength;
238 //"Authorization: Digest username="username", realm="myRealm", qop="auth",
239 //algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
241 segmentLength = strlen(NC_HDR);
242 if (*length < (currOffset + segmentLength)) {
245 memcpy(digestResponse + currOffset, NC_HDR, segmentLength);
246 currOffset += segmentLength;
248 //"Authorization: Digest username="username", realm="myRealm", qop="auth",
249 //algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
251 segmentLength = strlen(cnonce);
252 if (*length < (currOffset + segmentLength)) {
255 memcpy(digestResponse + currOffset, cnonce, segmentLength);
256 currOffset += segmentLength;
258 //"Authorization: Digest username="username", realm="myRealm", qop="auth",
259 //algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
260 //cnonce="ab341...", response="
261 segmentLength = strlen(RSP_HDR);
262 if (*length < (currOffset + segmentLength)) {
265 memcpy(digestResponse + currOffset, RSP_HDR, segmentLength);
266 currOffset += segmentLength;
269 DigestCalcHA1("MD5", userName, realm, password, nonce, cnonce, HA1);
270 DigestCalcResponse(HA1, nonce, "00000002", cnonce, "auth", method, uri,
273 //"Authorization: Digest username="username", realm="myRealm", qop="auth",
274 //algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
275 //cnonce="ab341...", response="8bbf2...
276 segmentLength = strlen(response);
277 if (*length < (currOffset + segmentLength)) {
280 memcpy(digestResponse + currOffset, response, segmentLength);
281 currOffset += segmentLength;
283 //"Authorization: Digest username="username", realm="myRealm", qop="auth",
284 //algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000002,
285 //cnonce="ab341...", response="8bbf2..."
286 if (*length < (currOffset + 2)) {
289 memcpy(digestResponse + currOffset, "\"", 1);
292 //add null termination
293 *(digestResponse+currOffset) = 0;
294 *length = currOffset;
300 * generate a 32 byte random hexadecimal string such as "4f6ba982..."
302 * outbuff - buffer to fill
304 void GenerateCNonce(char *outbuff)
306 srand((unsigned int)time(NULL));
307 for(int i = 0; i < 32; i++) {
308 int num = (int)(((double) rand()/ ((double)RAND_MAX+1)) * 16);
310 case 0: case 1: case 2: case 3: case 4: case 5:
311 case 6: case 7: case 8: case 9:
312 outbuff[i] = '0' + num;
314 case 10: case 11: case 12: case 13: case 14: case 15:
315 outbuff[i] = 'a' + (num-10);
325 * Creates the HTTP digest response
327 * data - the HTTP digest structure
328 * authHeader - the HTTP digest challenge
329 * responseLength - size of response buffer
330 * digestResponse - buffer for HTTP digest response
332 * Returns 0 for suceess. -1 for error
334 int CreateDigestResponse(struct http_digest_data *data, char *authHeader,
335 size_t *responseLength, char *digestResponse)
338 char *realmPtrStart, *realmPtrEnd, *noncePtrStart, *noncePtrEnd;
339 size_t segmentLength;
340 char realm[HDR_SIZE], nonce[HDR_SIZE];;
342 if (digestResponse == NULL || authHeader == NULL) {
346 GenerateCNonce(cnonce);
348 //grep realm from challange
349 realmPtrStart = strstr(authHeader, "realm=");
350 if (realmPtrStart == NULL) {
353 //point to start of realm
355 realmPtrEnd = strstr(realmPtrStart, "\"");
356 segmentLength = realmPtrEnd - realmPtrStart;
357 memcpy(realm, realmPtrStart, segmentLength);
358 //add NULL termination
359 realm[segmentLength] = 0;
361 //grep nonce from challange
362 noncePtrStart = strstr(authHeader, "nonce=");
363 if (noncePtrStart == NULL) {
366 //point to start of nonce
368 noncePtrEnd = strstr(noncePtrStart, "\"");
369 segmentLength = noncePtrEnd - noncePtrStart;
370 memcpy(nonce, noncePtrStart, segmentLength);
371 //add NULL termination
372 nonce[segmentLength]=0;
374 return CalculateResponse(data->username, data->password, data->method, realm,
375 data->requestUrl, nonce, cnonce,digestResponse, responseLength);
380 * Copies the contents of the plugin
382 * soap - pointer to the soap runtime environment
383 * dst - the destination plugin
384 * src - the original plugin
386 * Returns SOAP_OK for suceess. Error value for error
388 static int http_digest_copy(struct soap *soap, struct soap_plugin *dst,
389 struct soap_plugin *src)
392 dst->data = (void*)malloc(sizeof(struct http_digest_data));
396 memcpy(dst->data, src->data, sizeof(struct http_digest_data));
398 ((struct http_digest_data*)dst->data)->sendBuffer = NULL;
403 * Deletes the contents of the plugin
405 * soap - pointer to the soap runtime environment
408 static void http_digest_delete(struct soap *soap, struct soap_plugin *p)
410 struct http_digest_data *data =
411 (struct http_digest_data*)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
414 if (data->sendBuffer) {
415 free(data->sendBuffer);
422 * Open function. Will be called when connection opened. Used for saving parameters.
424 * soap - pointer to the soap runtime environment
426 * host - machine to connect to
427 * port - port on the host
429 * Returns SOAP_OK for suceess. Error value for error
431 static int http_digest_fopen(struct soap *soap, const char *endpoint, const char *host,
434 struct http_digest_data *data =
435 (struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
436 data->endpoint = (char*)endpoint;
437 data->host = (char*)host;
439 return data->fopen(soap, endpoint, host, port);
443 * Post header function. Used to identify parameters of the HTTP message
445 * soap - pointer to the soap runtime environment
446 * key - the header key
447 * value - the header value
449 * Returns SOAP_OK for suceess. Error value for error
451 static int http_digest_fposthdr(struct soap *soap, const char *key, const char *value)
453 struct http_digest_data *data =
454 (struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
457 // if this is the initial POST header then we initialize our send buffer
459 data->sendMsgSize = 0;
460 data->sendBufferLength = 0;
462 if (data->sendBuffer) {
463 free(data->sendBuffer);
464 data->sendBuffer = NULL;
467 data->password = soap->passwd;
468 data->username = soap->userid;
469 soap->passwd = soap->userid = NULL;
470 soap->frecv = http_digest_frecv;
472 // read the method and URI form the key
473 if (!strncmp(key, "GET", 3))
474 memcpy(data->method, "GET", 4);
475 else if (!strncmp(key, "POST", 4))
476 memcpy(data->method, "POST", 5);
478 return soap->error = SOAP_EOM;
480 s1 = strstr(key, " ");
482 return soap->error = SOAP_EOM;
486 s2 = strstr(s1, " ");
488 return soap->error = SOAP_EOM;
491 if (sizeof(data->requestUrl) <= (size_t)(s2 - s1)) {
492 return soap->error = SOAP_EOM;
494 memcpy(data->requestUrl, s1, s2-s1);
495 data->requestUrl[s2-s1] = '\0';
498 // determine the maximum length of this message so that we can
499 // correctly determine when we have completed the send
500 if (!strcmp(key, "Content-Length" )) {
501 data->sendMsgSize += strtoul(value, NULL, 10);
505 // calculate the header size
506 data->sendMsgSize += 2;
508 data->sendMsgSize += strlen(key);
510 data->sendMsgSize += (strlen(value) + 2);
513 return data->fposthdr(soap, key, value);
517 * Send function. Used to buffer the sent data and save for resends
519 * soap - pointer to the soap runtime environment
520 * buf - buffer to be sent
521 * size - size of data to send
523 * Returns SOAP_OK for suceess. Error value for error
525 static int http_digest_fsend(struct soap *soap, const char *buf, size_t size)
527 struct http_digest_data *data =
528 (struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
529 size_t newBufferLen = data->sendBufferLength + size;
531 if (!data->sendBuffer || (newBufferLen > data->sendMsgSize)) {
532 if (newBufferLen > data->sendMsgSize) {
533 data->sendMsgSize = newBufferLen;
535 data->sendBuffer = (char *)realloc(data->sendBuffer, data->sendMsgSize);
536 if (!data->sendBuffer) {
540 memcpy(data->sendBuffer + data->sendBufferLength, buf, size);
541 data->sendBufferLength = newBufferLen;
543 // if we haven't got the entire length of the message yet, then
544 // we return to gsoap and let it continue
545 if (data->sendBufferLength < data->sendMsgSize) {
549 // we've now got the entire message, now we can send the buffer
550 return data->fsend(soap, data->sendBuffer, data->sendBufferLength);
554 * Reads the next character. May need to read from the network
556 * data - pointer to the http digest structure
558 * Returns the next character or EOF for failure.
560 static char http_digest_getchar(struct http_digest_data *data)
563 if (data->rcvIdx < data->rcvRead)
564 return data->rcvBuffer[data->rcvIdx++];
565 res = data->frecv(data->soap, (data->rcvBuffer + data->rcvRead),
566 (data->rcvSize - data->rcvRead));
569 data->rcvRead += res;
570 return data->rcvBuffer[data->rcvIdx++];
574 * Reads the next HTTP header line.
576 * data - pointer to the http digest structure
577 * line - buffer to store the line read
578 * len - length of the line buffer
580 * Returns SOAP_OK for suceess. Error value for error.
582 static int http_digest_getline(struct http_digest_data *data, char *line, size_t len)
584 unsigned int i = len;
585 char c = 0, *s = line;
588 c = http_digest_getchar(data);
595 c = http_digest_getchar(data);
598 if (i+1 == len) // empty line: end of HTTP header
600 c = http_digest_getchar(data);
601 data->rcvIdx--; // return to previous character
602 if (c != ' ' && c != '\t') // HTTP line continuation?
604 } else if ((int)c == EOF)
611 * receive function. Used to look for digest authentication challenge.
612 * If the challenge is found will calculate the response and resend.
614 * soap - pointer to the soap runtime environment
615 * buf - buffer read data into
618 * Returns number of characters read. 0 for error.
620 static size_t http_digest_frecv(struct soap *soap, char *buf, size_t size) {
621 struct http_digest_data *data =
622 (struct http_digest_data *)soap_lookup_plugin(soap, HTTP_DIGEST_ID);
623 char header[HDR_SIZE], authResponse[HDR_SIZE];
624 static const char *CHALLANGE = "WWW-Authenticate: Digest";
625 static const unsigned int SYN_DELAY = 400000;
627 unsigned long httpStatus;
631 data->rcvBuffer = buf;
632 data->rcvSize = size;
637 if (http_digest_getline(data, header, HDR_SIZE))
639 if ((s = strchr(header, ' ')))
640 httpStatus = soap_strtoul(s, NULL, 10);
644 if ((httpStatus != 100) && (httpStatus != 401))
648 if (http_digest_getline(data, header, SOAP_HDRLEN))
654 if ((httpStatus == 401) && !strncmp(header, CHALLANGE, strlen(CHALLANGE))) {
659 } while (httpStatus == 100);
661 // if we got here httpStatus==401
665 // header is HTTP digest challenge
667 if (CreateDigestResponse(data, header, &len, authResponse))
670 s = strstr(data->sendBuffer, "\r\n");
674 s += 2; // point to the start of second line
676 // reset rcvRead so that if error occurs will return 0.
679 //delay sending SYN to allow AMT to close connection gracefully
682 soap->socket = data->fopen(soap, data->endpoint, data->host, data->port);
683 if (soap->error || !soap_valid_socket(soap->socket))
686 if (data->fsend(soap, data->sendBuffer, s-data->sendBuffer) ||
687 data->fsend(soap, authResponse, len) ||
688 data->fsend(soap, "\r\n", 2) ||
689 data->fsend(soap, s, data->sendBufferLength - (s-data->sendBuffer)))
692 // after send - send FIN if needed
694 if (!soap->ssl && soap_valid_socket(soap->socket) && !soap->keep_alive)
695 soap->fshutdownsocket(soap, (SOAP_SOCKET)soap->socket, 1); // Send TCP FIN
697 if (soap_valid_socket(soap->socket) && !soap->keep_alive)
698 soap->fshutdownsocket(soap, (SOAP_SOCKET)soap->socket, 1); // Send TCP FIN
701 data->rcvRead = data->frecv(soap, buf, size);
704 if (data->sendBuffer) {
705 free(data->sendBuffer);
706 data->sendBuffer = NULL;
708 soap->frecv = data->frecv;
709 return data->rcvRead;