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
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
21 * Very simple challenge-response, based on SHA1 + shared-secret.
24 #include <sys/types.h>
35 static pthread_mutex_t auth_mutex
= PTHREAD_MUTEX_INITIALIZER
;
36 static SHA1_CTX auth_sha1_ctx
;
40 static char __rdata
[KEY_SIZE
*4];
43 typedef struct _random_data
{
52 * Dump a buffer of bytes in human-readable hexadecimal to output.
54 * @param buf Buffer to dump
55 * @param len Length of buf
58 hex_dump(char *buf
, int len
)
64 printf("%02x", (int)(buf
[x
]&0x000000ff));
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.
81 mash(char *pd
, int psize
, char *rd
, int rsize
)
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
++)
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.
111 alloc_random_data(random_data_t
*data
, int size
)
113 pthread_mutex_lock(&auth_mutex
);
116 rfd
= open("/dev/random", O_RDONLY
);
118 pthread_mutex_unlock(&auth_mutex
);
121 read(rfd
, __rdata
, sizeof(__rdata
));
126 urfd
= open("/dev/urandom", O_RDONLY
);
132 pthread_mutex_unlock(&auth_mutex
);
135 pthread_mutex_unlock(&auth_mutex
);
138 data
->rd_size
= size
;
139 data
->rd_data
= malloc(size
);
140 pthread_mutex_unlock(&auth_mutex
);
146 /* Kind of depends on cryptographic strength
147 of /dev/urandom this way */
148 read(urfd
, (char *)(data
->rd_data
), size
);
150 mash((char *)data
->rd_data
, size
, __rdata
,
159 * Munmap/free data stored in a random_data_t structure.
161 * @param data random_data_t to clear out.
164 free_random_data(random_data_t
*data
)
171 * Initialize our global SHA1 context.
173 * @param key Key we're starting with.
174 * @param keylen Length of our key.
178 auth_sha1_init(char *key
, size_t keylen
)
180 pthread_mutex_lock(&auth_mutex
);
181 SHA1Init(&auth_sha1_ctx
);
184 SHA1Update(&auth_sha1_ctx
, (uint8_t *)key
, keylen
);
186 pthread_mutex_unlock(&auth_mutex
);
192 * Close our /dev/random FD & clear up our SHA1 Context.
195 auth_sha1_deinit(void)
197 pthread_mutex_lock(&auth_mutex
);
208 SHA1Init(&auth_sha1_ctx
);
209 pthread_mutex_unlock(&auth_mutex
);
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];
231 random_data_t challenge_data
;
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)
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
);
250 printf("Issuing challenge: ");
251 hex_dump(challenge_data
.rd_data
, challenge_data
.rd_size
);
253 printf("Expecting: ");
254 hex_dump(expected_sha1
, 20);
258 p
= challenge_data
.rd_data
;
260 n
= write(fd
, p
, remain
);
264 free_random_data(&challenge_data
);
268 free_random_data(&challenge_data
);
275 free_random_data(&challenge_data
);
281 printf("Challenge sent, waiting for response\n");
285 while (remain
&& (tv
.tv_sec
|| tv
.tv_usec
)) {
289 n
= select(fd
+1, &rfds
, NULL
, NULL
, &tv
);
300 n
= read(fd
, p
, remain
);
316 printf("Received: ");
317 hex_dump(client_sha1
, 20);
321 if (remain
|| memcmp(expected_sha1
, client_sha1
, 20)) {
323 reply
= (char)(0xff);
324 write(fd
, &reply
, 1);
330 printf("Authentication successful\n");
332 write(fd
, &reply
, 1);
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
351 int remain
= KEY_SIZE
, n
;
352 char key
[KEY_SIZE
], *p
;
358 pthread_mutex_lock(&auth_mutex
);
359 memcpy(&ch_ctx
, &auth_sha1_ctx
, sizeof(ch_ctx
));
360 pthread_mutex_unlock(&auth_mutex
);
363 printf("Awaiting challenge\n");
366 /* Grab the challenge from the server */
372 while (remain
&& (tv
.tv_sec
|| tv
.tv_usec
)) {
376 n
= select(fd
+1, &rfds
, NULL
, NULL
, &tv
);
387 n
= read(fd
, p
, remain
);
406 printf("Received: ");
407 hex_dump(key
, KEY_SIZE
);
411 SHA1Update(&ch_ctx
, (uint8_t *)key
, KEY_SIZE
);
412 SHA1Final(response
, &ch_ctx
);
417 n
= write(fd
, p
, remain
);
431 printf("Responded with: ");
432 hex_dump(response
, 20);
436 /* wait for reply from server as to whether or not we
439 while (tv
.tv_sec
|| tv
.tv_usec
) {
443 n
= select(fd
+1, &rfds
, NULL
, NULL
, &tv
);
471 /* Permission denied */
478 #include <arpa/inet.h>
483 main(int argc
, char **argv
)
486 int fd
, newfd
, len
, flag
, pid
;
487 struct sockaddr_in sai
;
490 printf("TCP auth_sha1 Echo Server\n");
492 fprintf(stderr
,"usage: %s <port> <password>\n", argv
[0]);
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
);
505 setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
,
506 &flag
, sizeof(flag
));
508 if (bind(fd
, (struct sockaddr
*)&sai
, sizeof(sai
)) == -1) {
513 if (listen(fd
, 5) == -1) {
520 newfd
= accept(fd
, (struct sockaddr
*)&sai
, &len
);
527 if (auth_sha1_challenge(newfd
) == -1) {
528 perror("auth_sha1_challenge");
544 printf("New Child: PID%d\n", pid
);
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
))) {
557 printf("[%d] Connection closed by remote host\n",
561 fprintf(stderr
, "[%d] read: %s", pid
,
565 write(newfd
, buf
, len
);
573 main(int argc
, char **argv
)
577 struct sockaddr_in sai
;
580 printf("TCP auth_sha1 Echo Client\n");
583 fprintf(stderr
, "usage: %s <port> <password>\n",
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) {
601 if (auth_sha1(fd
) != 0) {
606 printf("Authenticated. Type away.\n");
609 FD_SET(STDIN_FILENO
,&rfds
);
612 select(1024, &rfds
, NULL
, NULL
, NULL
);
614 if (FD_ISSET(STDIN_FILENO
,&rfds
)) {
615 len
= read(STDIN_FILENO
, buf
, sizeof(buf
));
618 printf("Connection closed by remote host\n");
622 if (write(fd
, buf
, len
) == -1) {
628 if (FD_ISSET(fd
, &rfds
)) {
629 memset(buf
,0,sizeof(buf
));
630 if (read(fd
, buf
, sizeof(buf
)) == -1) {