Clean up RPM spec for newer distributions
[clumanager.git] / librhcm / auth_sha1.c
blobf2f1fe5f76fbe7ac4016d6cd7b9f7199d5d53357
1 /*
2 Copyright Red Hat, Inc. 2003
4 The Red Hat Cluster Manager API Library is free software; you can
5 redistribute it and/or modify it under the terms of the GNU Lesser
6 General Public License as published by the Free Software Foundation;
7 either version 2.1 of the License, or (at your option) any later
8 version.
10 The Red Hat Cluster Manager API Library is distributed in the hope
11 that it will be useful, but WITHOUT ANY WARRANTY; without even the
12 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13 PURPOSE. See the GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18 USA.
20 /** @file
21 * Very simple challenge-response, based on SHA1 + shared-secret.
23 #include <sys/mman.h>
24 #include <sys/types.h>
25 #include <stdlib.h>
26 #include <fcntl.h>
27 #include <sha1.h>
28 #include <pthread.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <unistd.h>
33 #define KEY_SIZE 20
35 static pthread_mutex_t auth_mutex = PTHREAD_MUTEX_INITIALIZER;
36 static SHA1_CTX auth_sha1_ctx;
37 static int urfd = -1;
38 #if 0
39 static int rfd = -1;
40 static char __rdata[KEY_SIZE*4];
41 #endif
43 typedef struct _random_data {
44 int rd_size;
45 void *rd_data;
46 } random_data_t;
48 #ifdef DEBUG
49 #include <stdio.h>
51 /**
52 * Dump a buffer of bytes in human-readable hexadecimal to output.
54 * @param buf Buffer to dump
55 * @param len Length of buf
57 static void
58 hex_dump(char *buf, int len)
60 int x;
62 printf("0x");
63 for(x=0; x<len; x++)
64 printf("%02x", (int)(buf[x]&0x000000ff));
66 #endif
69 /**
70 * Mash pseudo random data with real (entropy) random data. This is so we
71 * don't run the system out of entropy when lots of authentication packets
72 * are being sent back and forth.
74 * @param pd Pseudo random data set.
75 * @param pdsize Pseudo random data set size.
76 * @param rd Random data set (based on system entropy).
77 * @param rdsize Random data set size.
79 #if 0
80 void
81 mash(char *pd, int psize, char *rd, int rsize)
83 int x;
84 int start = 0;
86 /* Pass 1: Generate pseudorandom start offset */
87 for (x = 0; x < psize; x++)
88 start ^= (int)(pd[x]&0xff);
89 start %= (rsize - psize + 1);
90 read(rfd, rd + (start % rsize), 1);
93 * Pass 2: XOR pseudo w/ random data starting
94 * at pseudorandom offset...
96 for (x = 0; x < psize; x++)
97 pd[x] ^= rd[x+start];
99 #endif
103 * Fudge random data based on /dev/urandom and previously
104 * read-in data from /dev/random.
106 * @param data random_data_t info to initialize.
107 * @param size Size of buffer (or amount to fill).
108 * @return -1 on failure; 0 on success.
110 static int
111 alloc_random_data(random_data_t *data, int size)
113 pthread_mutex_lock(&auth_mutex);
114 #if 0
115 if (rfd == -1) {
116 rfd = open("/dev/random", O_RDONLY);
117 if (rfd == -1) {
118 pthread_mutex_unlock(&auth_mutex);
119 return -1;
121 read(rfd, __rdata, sizeof(__rdata));
123 #endif
125 if (urfd == -1) {
126 urfd = open("/dev/urandom", O_RDONLY);
127 if (urfd == -1) {
128 #if 0
129 close(rfd);
130 rfd = -1;
131 #endif
132 pthread_mutex_unlock(&auth_mutex);
133 return -1;
135 pthread_mutex_unlock(&auth_mutex);
138 data->rd_size = size;
139 data->rd_data = malloc(size);
140 pthread_mutex_unlock(&auth_mutex);
142 if (!data->rd_data)
143 return -1;
145 if (size > 0) {
146 /* Kind of depends on cryptographic strength
147 of /dev/urandom this way */
148 read(urfd, (char *)(data->rd_data), size);
149 #if 0
150 mash((char *)data->rd_data, size, __rdata,
151 sizeof(__rdata));
152 #endif
154 return 0;
159 * Munmap/free data stored in a random_data_t structure.
161 * @param data random_data_t to clear out.
163 static void
164 free_random_data(random_data_t *data)
166 free(data->rd_data);
171 * Initialize our global SHA1 context.
173 * @param key Key we're starting with.
174 * @param keylen Length of our key.
175 * @return 0
178 auth_sha1_init(char *key, size_t keylen)
180 pthread_mutex_lock(&auth_mutex);
181 SHA1Init(&auth_sha1_ctx);
183 if (key && keylen)
184 SHA1Update(&auth_sha1_ctx, (uint8_t *)key, keylen);
186 pthread_mutex_unlock(&auth_mutex);
187 return 0;
192 * Close our /dev/random FD & clear up our SHA1 Context.
195 auth_sha1_deinit(void)
197 pthread_mutex_lock(&auth_mutex);
198 #if 0
199 if (rfd != -1) {
200 close(rfd);
201 rfd = -1;
203 #endif
204 if (urfd != -1) {
205 close(urfd);
206 urfd = -1;
208 SHA1Init(&auth_sha1_ctx);
209 pthread_mutex_unlock(&auth_mutex);
210 return 0;
215 * Issue SHA1 challenge. We basically just send some number of bytes read
216 * from /dev/random to the specified file descriptor. After we're done, we
217 * update a copy of our global key-SHA1 context, and compare what the
218 * client sends back with what we expect it to send back, based on our
219 * view of the secret key. If the client sends back the proper message,
220 * we report 'success'; otherwise, 'permission denied'.
222 * @param fd File descriptor to authenticate.
223 * @return -1 on fail/not authentic, 0 on success/authentic
226 auth_sha1_challenge(int fd)
228 int remain = KEY_SIZE, n;
229 uint8_t expected_sha1[20], client_sha1[20];
230 char *p, reply = 0;
231 random_data_t challenge_data;
232 SHA1_CTX ch_ctx;
233 struct timeval tv;
234 fd_set rfds;
236 pthread_mutex_lock(&auth_mutex);
237 memcpy(&ch_ctx, &auth_sha1_ctx, sizeof(ch_ctx));
238 pthread_mutex_unlock(&auth_mutex);
240 /* No random data, no authentication */
241 if (alloc_random_data(&challenge_data, KEY_SIZE) < 0)
242 return -1;
244 /* Figure out what we expect to receive */
245 SHA1Update(&ch_ctx, (uint8_t *)challenge_data.rd_data,
246 challenge_data.rd_size);
247 SHA1Final(expected_sha1, &ch_ctx);
249 #ifdef DEBUG
250 printf("Issuing challenge: ");
251 hex_dump(challenge_data.rd_data, challenge_data.rd_size);
252 printf("\n");
253 printf("Expecting: ");
254 hex_dump(expected_sha1, 20);
255 printf("\n");
256 #endif
258 p = challenge_data.rd_data;
259 while (remain) {
260 n = write(fd, p, remain);
261 if (n == -1) {
262 if (errno == EINTR)
263 continue;
264 free_random_data(&challenge_data);
265 return -1;
267 if (n == 0) {
268 free_random_data(&challenge_data);
269 return -1;
272 p += n;
273 remain -= n;
275 free_random_data(&challenge_data);
277 tv.tv_sec = 1;
278 tv.tv_usec = 0;
279 remain = KEY_SIZE;
280 #ifdef DEBUG
281 printf("Challenge sent, waiting for response\n");
282 #endif
284 p = client_sha1;
285 while (remain && (tv.tv_sec || tv.tv_usec)) {
286 FD_ZERO(&rfds);
287 FD_SET(fd, &rfds);
289 n = select(fd+1, &rfds, NULL, NULL, &tv);
290 if (n == -1) {
291 if (errno == EINTR)
292 continue;
293 return -1;
295 if (n == 0) {
296 errno = ETIMEDOUT;
297 return -1;
300 n = read(fd, p, remain);
301 if (n == -1) {
302 if (errno == EINTR)
303 continue;
304 return -1;
306 if (n == 0) {
307 errno = EPIPE;
308 return -1;
311 p += n;
312 remain -= n;
315 #ifdef DEBUG
316 printf("Received: ");
317 hex_dump(client_sha1, 20);
318 printf("\n");
319 #endif
321 if (remain || memcmp(expected_sha1, client_sha1, 20)) {
322 /* Notify client */
323 reply = (char)(0xff);
324 write(fd, &reply, 1);
325 errno = EACCES;
326 return -1;
329 #ifdef DEBUG
330 printf("Authentication successful\n");
331 #endif
332 write(fd, &reply, 1);
333 return 0;
338 * Handle an SHA1 challenge from a server. This is generally done
339 * immediately following a connect() call. This handles the challenge
340 * data from the server, updates an SHA1 context based on our "secret"
341 * key, and sends the resulting SHA1 back to the server. If the server
342 * accepts our response, it writes a null byte to us; anything else
343 * is assumed to be a rejection.
345 * @param fd File descriptor to authenticate.
346 * @return -1 on failure/auth-failed, 0 on success
349 auth_sha1(int fd)
351 int remain = KEY_SIZE, n;
352 char key[KEY_SIZE], *p;
353 char response[20];
354 SHA1_CTX ch_ctx;
355 struct timeval tv;
356 fd_set rfds;
358 pthread_mutex_lock(&auth_mutex);
359 memcpy(&ch_ctx, &auth_sha1_ctx, sizeof(ch_ctx));
360 pthread_mutex_unlock(&auth_mutex);
362 #ifdef DEBUG
363 printf("Awaiting challenge\n");
364 #endif
366 /* Grab the challenge from the server */
367 tv.tv_sec = 5;
368 tv.tv_usec = 0;
369 remain = KEY_SIZE;
370 p = key;
372 while (remain && (tv.tv_sec || tv.tv_usec)) {
373 FD_ZERO(&rfds);
374 FD_SET(fd, &rfds);
376 n = select(fd+1, &rfds, NULL, NULL, &tv);
377 if (n == -1) {
378 if (errno == EINTR)
379 continue;
380 return -1;
382 if (n == 0) {
383 errno = ETIMEDOUT;
384 return -1;
387 n = read(fd, p, remain);
388 if (n == -1) {
389 if (errno == EINTR)
390 continue;
391 return -1;
393 if (n == 0) {
394 errno = EPIPE;
395 return -1;
398 remain -= n;
399 p += n;
402 if (remain)
403 return -1;
405 #ifdef DEBUG
406 printf("Received: ");
407 hex_dump(key, KEY_SIZE);
408 printf("\n");
409 #endif
410 /* Build response */
411 SHA1Update(&ch_ctx, (uint8_t *)key, KEY_SIZE);
412 SHA1Final(response, &ch_ctx);
414 remain = 20;
415 p = response;
416 while (remain) {
417 n = write(fd, p, remain);
418 if (n == -1) {
419 if (errno == EINTR)
420 continue;
421 return -1;
423 if (n == 0)
424 return -1;
426 p += n;
427 remain -= n;
430 #ifdef DEBUG
431 printf("Responded with: ");
432 hex_dump(response, 20);
433 printf("\n");
434 #endif
436 /* wait for reply from server as to whether or not we
437 are authenticated */
438 p = key; /* Reuse */
439 while (tv.tv_sec || tv.tv_usec) {
440 FD_ZERO(&rfds);
441 FD_SET(fd, &rfds);
443 n = select(fd+1, &rfds, NULL, NULL, &tv);
444 if (n == -1) {
445 if (errno == EINTR)
446 continue;
447 return -1;
449 if (n == 0) {
450 errno = ETIMEDOUT;
451 return -1;
454 n = read(fd, p, 1);
455 if (n == -1) {
456 if (errno == EINTR)
457 continue;
458 return -1;
460 if (n == 0) {
461 errno = EPIPE;
462 return -1;
464 break;
467 if (*p == 0) {
468 return 0;
471 /* Permission denied */
472 errno = EACCES;
473 return -1;
477 #ifdef STANDALONE
478 #include <arpa/inet.h>
479 #include <stdio.h>
481 #ifdef SERVER
483 main(int argc, char **argv)
485 char buf[256];
486 int fd, newfd, len, flag, pid;
487 struct sockaddr_in sai;
488 fd_set rfds;
490 printf("TCP auth_sha1 Echo Server\n");
491 if (argc < 3) {
492 fprintf(stderr,"usage: %s <port> <password>\n", argv[0]);
493 return 1;
496 auth_sha1_init(argv[2], strlen(argv[2]));
498 fd = socket(PF_INET, SOCK_STREAM, 0);
499 memset(&sai, 0, sizeof(sai));
500 sai.sin_family = PF_INET;
501 sai.sin_port = htons(atoi(argv[1]));
502 sai.sin_addr.s_addr = htonl(INADDR_ANY);
504 flag = 1;
505 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
506 &flag, sizeof(flag));
508 if (bind(fd, (struct sockaddr *)&sai, sizeof(sai)) == -1) {
509 perror("bind");
510 return(-1);
513 if (listen(fd, 5) == -1) {
514 perror("listen");
515 return(-1);
518 len = sizeof(sai);
519 while (1) {
520 newfd = accept(fd, (struct sockaddr *)&sai, &len);
522 if (newfd == -1) {
523 perror("accept");
524 return(-1);
527 if (auth_sha1_challenge(newfd) == -1) {
528 perror("auth_sha1_challenge");
529 close(newfd);
530 continue;
533 pid = fork();
534 if (pid == 0) {
535 close(fd);
536 break;
539 if (pid == -1) {
540 perror("fork");
541 continue;
544 printf("New Child: PID%d\n", pid);
545 close(newfd);
548 pid = getpid();
549 while(1) {
550 FD_ZERO(&rfds);
551 FD_SET(newfd, &rfds);
553 if (select(newfd + 1, &rfds, NULL, NULL, NULL) == 1) {
554 memset(buf,0,sizeof(buf));
555 switch(len = read(newfd, buf, sizeof(buf))) {
556 case 0:
557 printf("[%d] Connection closed by remote host\n",
558 pid);
559 exit(0);
560 case -1:
561 fprintf(stderr, "[%d] read: %s", pid,
562 strerror(errno));
563 return(0);
564 default:
565 write(newfd, buf, len);
570 #else
573 main(int argc, char **argv)
575 char buf[256];
576 int fd, len;
577 struct sockaddr_in sai;
578 fd_set rfds;
580 printf("TCP auth_sha1 Echo Client\n");
582 if (argc < 3) {
583 fprintf(stderr, "usage: %s <port> <password>\n",
584 argv[0]);
585 return 1;
588 auth_sha1_init(argv[2], strlen(argv[2]));
590 fd = socket(PF_INET, SOCK_STREAM, 0);
591 memset(&sai, 0, sizeof(sai));
592 sai.sin_family = PF_INET;
593 sai.sin_port = htons(atoi(argv[1]));
594 sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
596 if (connect(fd, (struct sockaddr *)&sai, sizeof(sai)) == -1) {
597 perror("connect");
598 return(-1);
601 if (auth_sha1(fd) != 0) {
602 perror("auth_sha1");
603 return(-1);
606 printf("Authenticated. Type away.\n");
607 while(1) {
608 FD_ZERO(&rfds);
609 FD_SET(STDIN_FILENO,&rfds);
610 FD_SET(fd,&rfds);
612 select(1024, &rfds, NULL, NULL, NULL);
614 if (FD_ISSET(STDIN_FILENO,&rfds)) {
615 len = read(STDIN_FILENO, buf, sizeof(buf));
617 if (!len) {
618 printf("Connection closed by remote host\n");
619 return(0);
622 if (write(fd, buf, len) == -1) {
623 perror("write");
624 return(-1);
628 if (FD_ISSET(fd, &rfds)) {
629 memset(buf,0,sizeof(buf));
630 if (read(fd, buf, sizeof(buf)) == -1) {
631 perror("read");
632 return(-1);
635 printf("%s", buf);
639 return(0);
641 #endif
642 #endif