1/* 2 * Cheesy HTTP 1.1 client used for testing HTTP Digest (RFC 2617) 3 * variant of DIGEST-MD5 plugin 4 * 5 * XXX This client REQUIRES a persistent connection and 6 * WILL NOT accept a body in any HTTP response 7 */ 8 9#include <stdio.h> 10#include <stdlib.h> 11#include <string.h> 12#include <unistd.h> 13#include <pwd.h> 14 15#include <sys/types.h> 16#include <sys/socket.h> 17#include <netinet/in.h> 18#include <arpa/inet.h> 19#include <netdb.h> 20 21#include <sasl/sasl.h> 22 23#define SUCCESS 0 24#define ERROR 1 25 26#define BUFFER_SIZE 8192 27 28#define DIGEST_AUTH_HEADER "\r\nWWW-Authenticate: Digest " 29#define DIGEST_OK_HEADER "\r\nAuthentication-Info: " 30 31void interact(sasl_interact_t *ilist) 32{ 33 while (ilist->id != SASL_CB_LIST_END) { 34 switch (ilist->id) { 35 36 case SASL_CB_AUTHNAME: /* auth as current uid */ 37 ilist->result = strdup(getpwuid(getuid())->pw_name); 38 break; 39 40 case SASL_CB_PASS: /* prompt for password */ 41 printf("%s: ", ilist->prompt); 42 ilist->result = strdup(getpass("")); 43 break; 44 } 45 ilist->len = strlen(ilist->result); 46 47 ilist++; 48 } 49} 50 51int main(int argc __attribute__((unused)), char *argv[]) 52{ 53 const char *hostname = "localhost"; 54 int port = 80; 55 56 int sd, rc, status; 57 struct sockaddr_in localAddr, servAddr; 58 struct hostent *h; 59 60 const char *sasl_impl, *sasl_ver; 61 sasl_conn_t *saslconn; 62 sasl_interact_t *interactions = NULL; 63 sasl_security_properties_t secprops = { 0, /* min SSF ("auth") */ 64 1, /* max SSF ("auth-int") */ 65 0, /* don't need maxbuf */ 66 0, /* security flags */ 67 NULL, 68 NULL }; 69 sasl_http_request_t httpreq = { "HEAD", /* Method */ 70 "/", /* URI */ 71 (u_char *) "", /* Empty body */ 72 0, /* Zero-length body */ 73 0 }; /* Persistent cxn */ 74 sasl_callback_t callbacks[] = { 75 { SASL_CB_AUTHNAME, NULL, NULL }, 76 { SASL_CB_PASS, NULL, NULL }, 77 { SASL_CB_LIST_END, NULL, NULL } 78 }; 79 80 const char *response = NULL; 81 unsigned int resplen = 0; 82 char buffer[BUFFER_SIZE+1], *request, *challenge, *p; 83 int i, code; 84 85 printf("\n-- Hostname = %s , Port = %d , URI = %s\n", 86 hostname, port, httpreq.uri); 87 88 h = gethostbyname(hostname); 89 if(h == NULL) { 90 printf("unknown host: %s \n ", hostname); 91 exit(ERROR); 92 } 93 94 servAddr.sin_family = h->h_addrtype; 95 memcpy((char *) &servAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length); 96 servAddr.sin_port = htons(port); 97 98 /* create socket */ 99 printf("-- Create socket... "); 100 sd = socket(AF_INET, SOCK_STREAM, 0); 101 if (sd < 0) { 102 perror("cannot open socket "); 103 exit(ERROR); 104 } 105 106 /* bind port number */ 107 printf("Bind port number... "); 108 109 localAddr.sin_family = AF_INET; 110 localAddr.sin_addr.s_addr = htonl(INADDR_ANY); 111 localAddr.sin_port = htons(0); 112 113 rc = bind(sd, (struct sockaddr *) &localAddr, sizeof(localAddr)); 114 if (rc < 0) { 115 printf("%s: cannot bind port TCP %u\n",argv[0],port); 116 perror("error "); 117 exit(ERROR); 118 } 119 120 /* connect to server */ 121 printf("Connect to server...\n"); 122 rc = connect(sd, (struct sockaddr *) &servAddr, sizeof(servAddr)); 123 if (rc < 0) { 124 perror("cannot connect "); 125 exit(ERROR); 126 } 127 128 /* get SASL version info */ 129 sasl_version_info(&sasl_impl, &sasl_ver, NULL, NULL, NULL, NULL); 130 131 /* initialize client-side of SASL */ 132 status = sasl_client_init(callbacks); 133 134 /* request the URI twice, so we test both initial auth and reauth */ 135 for (i = 0; i < 2; i++) { 136 /* initialize a client exchange 137 * 138 * SASL_NEED_HTTP: forces HTTP Digest mode (REQUIRED) 139 * SASL_SUCCESS_DATA: HTTP supports success data in 140 * Authentication-Info header (REQUIRED) 141 */ 142 status = sasl_client_new("http", hostname, NULL, NULL, NULL, 143 SASL_NEED_HTTP | SASL_SUCCESS_DATA, &saslconn); 144 if (status != SASL_OK) { 145 perror("sasl_client_new() failed "); 146 exit(ERROR); 147 } 148 149 /* Set security peoperties as specified above */ 150 sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops); 151 152 /* Set HTTP request as specified above (REQUIRED) */ 153 sasl_setprop(saslconn, SASL_HTTP_REQUEST, &httpreq); 154 155 do { 156 /* start the Digest exchange */ 157 status = sasl_client_start(saslconn, "DIGEST-MD5", &interactions, 158 &response, &resplen, NULL); 159 if (status == SASL_INTERACT) interact(interactions); 160 } while (status == SASL_INTERACT); 161 162 if ((status != SASL_OK) && (status != SASL_CONTINUE)) { 163 perror("sasl_client_start() failed "); 164 exit(ERROR); 165 } 166 167 do { 168 /* send request (with Auth data if we have it ) */ 169 request = buffer; 170 request += sprintf(request, "%s %s HTTP/1.1\r\n", 171 httpreq.method, httpreq.uri); 172 request += sprintf(request, "Host: %s\r\n", hostname); 173 request += sprintf(request, "User-Agent: HTTP Digest Test Client" 174 " (%s/%s)\r\n", sasl_impl, sasl_ver); 175 request += sprintf(request, "Connection: keep-alive\r\n"); 176 request += sprintf(request, "Keep-Alive: 300\r\n"); 177 if (response) { 178 request += sprintf(request, "Authorization: Digest %s\r\n", 179 response); 180 } 181 request += sprintf(request, "\r\n"); 182 request = buffer; 183 184 printf("\n-- Send HTTP request:\n\n%s", request); 185 rc = write(sd, request, strlen(request)); 186 if (rc < 0) { 187 perror("cannot send data "); 188 close(sd); 189 exit(ERROR); 190 } 191 192 /* display response */ 193 printf("-- Received response:\n\tfrom server: http://%s%s, IP = %s,\n\n", 194 hostname, httpreq.uri, inet_ntoa(servAddr.sin_addr)); 195 rc = read(sd, buffer, BUFFER_SIZE); 196 if (rc <= 0) { 197 perror("cannot read data "); 198 close(sd); 199 exit(ERROR); 200 } 201 buffer[rc] = '\0'; 202 203 printf("%s", buffer); 204 205 /* get response code */ 206 sscanf(buffer, "HTTP/1.1 %d ", &code); 207 208 if (code == 401) { 209 /* find Digest challenge */ 210 challenge = strstr(buffer, DIGEST_AUTH_HEADER); 211 if (!challenge) break; 212 challenge += strlen(DIGEST_AUTH_HEADER); 213 p = strchr(challenge, '\r'); 214 *p = '\0'; 215 216 do { 217 /* do the next step in the exchange */ 218 status = sasl_client_step(saslconn, 219 challenge, strlen(challenge), 220 &interactions, 221 &response, &resplen); 222 if (status == SASL_INTERACT) interact(interactions); 223 } while (status == SASL_INTERACT); 224 225 if ((status != SASL_OK) && (status != SASL_CONTINUE)) { 226 perror("sasl_client_step failed "); 227 exit(ERROR); 228 } 229 } 230 } while (code == 401); 231 232 if ((code == 200) && (status == SASL_CONTINUE)) { 233 /* find Digest response */ 234 challenge = strstr(buffer, DIGEST_OK_HEADER); 235 if (challenge) { 236 challenge += strlen(DIGEST_OK_HEADER); 237 p = strchr(challenge, '\r'); 238 *p = '\0'; 239 240 /* do the final step in the exchange (server auth) */ 241 status = sasl_client_step(saslconn, 242 challenge, strlen(challenge), 243 &interactions, &response, &resplen); 244 } 245 } 246 247 sasl_dispose(&saslconn); 248 } 249 250 sasl_client_done(); 251 252 close(sd); 253 return SUCCESS; 254} 255