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 MD5 + shared-secret.
22 * Uses Colin Plumb's MD5 code.
25 #include <sys/types.h>
34 #define AUTH_TIMEOUT 5
37 static pthread_mutex_t auth_mutex
= PTHREAD_MUTEX_INITIALIZER
;
38 static int auth_enabled
= 0;
39 static struct MD5Context auth_md5_ctx
;
43 static char __rdata
[KEY_SIZE
*4];
46 typedef struct _random_data
{
55 * Dump a buffer of bytes in human-readable hexadecimal to output.
57 * @param buf Buffer to dump
58 * @param len Length of buf
61 hex_dump(char *buf
, int len
)
67 printf("%02x", (int)(buf
[x
]&0x000000ff));
73 * Mash pseudo random data with real (entropy) random data. This is so we
74 * don't run the system out of entropy when lots of authentication packets
75 * are being sent back and forth.
77 * @param pd Pseudo random data set.
78 * @param pdsize Pseudo random data set size.
79 * @param rd Random data set (based on system entropy).
80 * @param rdsize Random data set size.
84 mash(char *pd
, int psize
, char *rd
, int rsize
)
89 /* Pass 1: Generate pseudorandom start offset */
90 for (x
= 0; x
< psize
; x
++)
91 start
^= (int)(pd
[x
]&0xff);
92 start
%= (rsize
- psize
+ 1);
93 read(rfd
, rd
+ (start
% rsize
), 1);
96 * Pass 2: XOR pseudo w/ random data starting
97 * at pseudorandom offset...
99 for (x
= 0; x
< psize
; x
++)
100 pd
[x
] ^= rd
[x
+start
];
106 * Fudge random data based on /dev/urandom and previously
107 * read-in data from /dev/random.
109 * @param data random_data_t info to initialize.
110 * @param size Size of buffer (or amount to fill).
111 * @return -1 on failure; 0 on success.
114 alloc_random_data(random_data_t
*data
, int size
)
116 pthread_mutex_lock(&auth_mutex
);
119 rfd
= open("/dev/random", O_RDONLY
);
121 pthread_mutex_unlock(&auth_mutex
);
124 read(rfd
, __rdata
, sizeof(__rdata
));
129 urfd
= open("/dev/urandom", O_RDONLY
);
135 pthread_mutex_unlock(&auth_mutex
);
138 pthread_mutex_unlock(&auth_mutex
);
141 data
->rd_size
= size
;
142 data
->rd_data
= malloc(size
);
143 pthread_mutex_unlock(&auth_mutex
);
149 /* Kind of depends on cryptographic strength
150 of /dev/urandom this way */
151 read(urfd
, (char *)(data
->rd_data
), size
);
153 mash((char *)data
->rd_data
, size
, __rdata
,
162 * Munmap/free data stored in a random_data_t structure.
164 * @param data random_data_t to clear out.
167 free_random_data(random_data_t
*data
)
174 * Initialize our global MD5 context.
176 * @param key Key we're starting with.
177 * @param keylen Length of our key.
181 auth_md5_init(char *key
, size_t keylen
)
183 pthread_mutex_lock(&auth_mutex
);
184 MD5Init(&auth_md5_ctx
);
187 MD5Update(&auth_md5_ctx
, (uint8_t *)key
, keylen
);
193 pthread_mutex_unlock(&auth_mutex
);
199 * Close our /dev/random FD & clear up our MD5 Context.
202 auth_md5_deinit(void)
204 pthread_mutex_lock(&auth_mutex
);
215 MD5Init(&auth_md5_ctx
);
217 pthread_mutex_unlock(&auth_mutex
);
223 * Issue MD5 challenge. We basically just send some number of bytes read
224 * from /dev/random to the specified file descriptor. After we're done, we
225 * update a copy of our global key-MD5 context, and compare what the
226 * client sends back with what we expect it to send back, based on our
227 * view of the secret key. If the client sends back the proper message,
228 * we report 'success'; otherwise, 'permission denied'.
230 * @param fd File descriptor to authenticate.
231 * @return -1 on fail/not authentic, 0 on success/authentic
234 auth_md5_challenge(int fd
)
236 int remain
= KEY_SIZE
, n
;
237 uint8_t expected_md5
[16], client_md5
[16];
238 unsigned char *p
, reply
= 0;
239 random_data_t challenge_data
;
240 struct MD5Context ch_ctx
;
244 pthread_mutex_lock(&auth_mutex
);
246 pthread_mutex_unlock(&auth_mutex
);
250 memcpy(&ch_ctx
, &auth_md5_ctx
, sizeof(ch_ctx
));
251 pthread_mutex_unlock(&auth_mutex
);
253 /* No random data, no authentication */
254 if (alloc_random_data(&challenge_data
, KEY_SIZE
) < 0)
257 /* Figure out what we expect to receive */
258 MD5Update(&ch_ctx
, (uint8_t *)challenge_data
.rd_data
,
259 challenge_data
.rd_size
);
260 MD5Final(expected_md5
, &ch_ctx
);
263 printf("Issuing challenge: ");
264 hex_dump(challenge_data
.rd_data
, challenge_data
.rd_size
);
266 printf("Expecting: ");
267 hex_dump(expected_md5
, 16);
271 p
= challenge_data
.rd_data
;
273 n
= write(fd
, p
, remain
);
277 free_random_data(&challenge_data
);
281 free_random_data(&challenge_data
);
288 free_random_data(&challenge_data
);
290 tv
.tv_sec
= AUTH_TIMEOUT
;
294 printf("Challenge sent, waiting for response\n");
298 while (remain
&& (tv
.tv_sec
|| tv
.tv_usec
)) {
302 n
= select(fd
+1, &rfds
, NULL
, NULL
, &tv
);
313 n
= read(fd
, p
, remain
);
329 printf("Received: ");
330 hex_dump(client_md5
, 16);
334 if (remain
|| memcmp(expected_md5
, client_md5
, 16)) {
336 reply
= (char)(0xff);
337 write(fd
, &reply
, 1);
343 printf("Authentication successful\n");
345 write(fd
, &reply
, 1);
351 * Handle an MD5 challenge from a server. This is generally done
352 * immediately following a connect() call. This handles the challenge
353 * data from the server, updates an MD5 context based on our "secret"
354 * key, and sends the resulting MD5 back to the server. If the server
355 * accepts our response, it writes a null byte to us; anything else
356 * is assumed to be a rejection.
358 * @param fd File descriptor to authenticate.
359 * @return -1 on failure/auth-failed, 0 on success
364 int remain
= KEY_SIZE
, n
;
365 unsigned char key
[KEY_SIZE
];
367 unsigned char response
[16];
368 struct MD5Context ch_ctx
;
372 pthread_mutex_lock(&auth_mutex
);
374 pthread_mutex_unlock(&auth_mutex
);
378 memcpy(&ch_ctx
, &auth_md5_ctx
, sizeof(ch_ctx
));
379 pthread_mutex_unlock(&auth_mutex
);
382 printf("Awaiting challenge\n");
385 /* Grab the challenge from the server */
391 while (remain
&& (tv
.tv_sec
|| tv
.tv_usec
)) {
395 n
= select(fd
+1, &rfds
, NULL
, NULL
, &tv
);
406 n
= read(fd
, p
, remain
);
425 printf("Received: ");
426 hex_dump(key
, KEY_SIZE
);
430 MD5Update(&ch_ctx
, (uint8_t *)key
, KEY_SIZE
);
431 MD5Final(response
, &ch_ctx
);
436 n
= write(fd
, p
, remain
);
450 printf("Responded with: ");
451 hex_dump(response
, 16);
455 /* wait for reply from server as to whether or not we
458 while (tv
.tv_sec
|| tv
.tv_usec
) {
462 n
= select(fd
+1, &rfds
, NULL
, NULL
, &tv
);
490 /* Permission denied */
497 #include <arpa/inet.h>
502 main(int argc
, char **argv
)
505 int fd
, newfd
, len
, flag
, pid
;
506 struct sockaddr_in sai
;
509 printf("TCP auth_md5 Echo Server\n");
511 fprintf(stderr
,"usage: %s <port> [password]\n", argv
[0]);
516 auth_md5_init(argv
[2], strlen(argv
[2]));
518 auth_md5_init(NULL
, 0);
520 fd
= socket(PF_INET
, SOCK_STREAM
, 0);
521 memset(&sai
, 0, sizeof(sai
));
522 sai
.sin_family
= PF_INET
;
523 sai
.sin_port
= htons(atoi(argv
[1]));
524 sai
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
527 setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
,
528 &flag
, sizeof(flag
));
530 if (bind(fd
, (struct sockaddr
*)&sai
, sizeof(sai
)) == -1) {
535 if (listen(fd
, 5) == -1) {
542 newfd
= accept(fd
, (struct sockaddr
*)&sai
, &len
);
549 if (auth_md5_challenge(newfd
) == -1) {
550 perror("auth_md5_challenge");
566 printf("New Child: PID%d\n", pid
);
573 FD_SET(newfd
, &rfds
);
575 if (select(newfd
+ 1, &rfds
, NULL
, NULL
, NULL
) == 1) {
576 memset(buf
,0,sizeof(buf
));
577 switch(len
= read(newfd
, buf
, sizeof(buf
))) {
579 printf("[%d] Connection closed by remote host\n",
583 fprintf(stderr
, "[%d] read: %s", pid
,
587 write(newfd
, buf
, len
);
595 main(int argc
, char **argv
)
599 struct sockaddr_in sai
;
602 printf("TCP auth_md5 Echo Client\n");
605 fprintf(stderr
, "usage: %s <port> [password]\n",
611 auth_md5_init(argv
[2], strlen(argv
[2]));
613 auth_md5_init(NULL
, 0);
615 fd
= socket(PF_INET
, SOCK_STREAM
, 0);
616 memset(&sai
, 0, sizeof(sai
));
617 sai
.sin_family
= PF_INET
;
618 sai
.sin_port
= htons(atoi(argv
[1]));
619 sai
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
621 if (connect(fd
, (struct sockaddr
*)&sai
, sizeof(sai
)) == -1) {
626 if (auth_md5(fd
) != 0) {
631 printf("Authenticated. Type away.\n");
634 FD_SET(STDIN_FILENO
,&rfds
);
637 select(1024, &rfds
, NULL
, NULL
, NULL
);
639 if (FD_ISSET(STDIN_FILENO
,&rfds
)) {
640 len
= read(STDIN_FILENO
, buf
, sizeof(buf
));
643 printf("Connection closed by remote host\n");
647 if (write(fd
, buf
, len
) == -1) {
653 if (FD_ISSET(fd
, &rfds
)) {
654 memset(buf
,0,sizeof(buf
));
655 if (read(fd
, buf
, sizeof(buf
)) == -1) {