/*- * Copyright (c) 2005-2006 Ulf Lilleengen * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include "auth.h" #include "misc.h" #include "proto.h" #include "queue.h" #include "stream.h" struct auth_record { char *client; char *passwd; char *server; STAILQ_ENTRY(auth_record) link; }; struct auth { STAILQ_HEAD(, auth_record) head; }; static int auth_parse(struct auth_record *, char *); static void tolowercase(char *); /* * Read records from the AUTHF configuration file, parse the records, and create * a STAILQ list from the records. Returns a structure containing the head of * the list, used by functions later on to get more information from the * records. */ struct auth * auth_read_records(void) { struct stream *s; struct auth *a; struct auth_record *r; struct passwd *me; char *buf, *fname; a = NULL; /* Get filename. This code is not threaded, so using getuid should be * okay. */ if ((me = getpwuid(getuid())) == NULL) { lprintf(-1, "Could not get password file entry\n"); return (NULL); } xasprintf(&fname, "%s%s", me->pw_dir, AUTHF); /* Open the auth file */ s = stream_open_file(fname, O_RDONLY); if (s == NULL) { lprintf(2, "Error opening file %s\n", fname); goto bad; } a = xmalloc(sizeof(struct auth)); STAILQ_INIT(&a->head); while ( (buf = stream_getln(s, NULL)) != NULL) { r = xmalloc(sizeof(struct auth_record)); r->client = NULL; r->server = NULL; r->passwd = NULL; if (buf[0] == '#') continue; /* If failing, behave like cvsup. */ if (auth_parse(r, buf) == -1) { lprintf(-1, "Error in authentication file " AUTHF "\n"); goto bad; } auth_parse(r, buf); STAILQ_INSERT_TAIL(&a->head, r, link); } bad: stream_close(s); free(fname); return (a); } /* Parses buffer and inserts in appropriate fields */ static int auth_parse(struct auth_record *r, char *line) { char *tok; if ((tok = strsep(&line, ":")) == NULL) goto bad; r->server = xstrdup(tok); if ((tok = strsep(&line, ":")) == NULL) goto bad; r->client = xstrdup(tok); if ((tok = strsep(&line, ":")) == NULL) goto bad; r->passwd = xstrdup(tok); return (0); bad: return (-1); } /* Free address list */ void auth_free_records(struct auth *a) { struct auth_record *r1, *r2; if (!a) return; r1 = STAILQ_FIRST(&a->head); while (r1 != NULL) { r2 = STAILQ_NEXT(r1, link); if (r1->server) free(r1->server); if (r1->client) free(r1->client); if (r1->passwd) free(r1->passwd); free(r1); r1 = r2; } free(a); } struct auth_record * auth_lookup(struct auth *a, char *hostname) { struct auth_record *cur; STAILQ_FOREACH(cur, &a->head, link) { if (strcmp(cur->server, hostname) == 0) return (cur); } return (NULL); } /* * Create secret hash from the clientname, servername and password defined in * AUTHF configuration file. */ char * auth_makesecret(struct auth_record *r) { MD5_CTX c; char md5[MD5_DIGEST_SIZE]; char *secret; tolowercase(r->server); tolowercase(r->client); MD5_Init(&c); MD5_Update(&c, r->client, strlen(r->client)); MD5_Update(&c, ":", 1); MD5_Update(&c, r->server, strlen(r->server)); MD5_Update(&c, ":", 1); MD5_Update(&c, r->passwd, strlen(r->passwd)); MD5_End(md5, &c); xasprintf(&secret, "$md5$%s", md5); return (secret); } /* * Generate the response from the challenge of the server, and the shared * secret. */ char * auth_genresponse(char *challenge, char *shared_secret) { MD5_CTX c; char buf[MD5_DIGEST_SIZE]; char *response; MD5_Init(&c); MD5_Update(&c, shared_secret, strlen(shared_secret)); MD5_Update(&c, ":", 1); MD5_Update(&c, challenge, strlen(challenge)); MD5_End(buf, &c); xasprintf(&response, "%s", buf); return (response); } /* * Generate challenge to authenticate server. First seeds the random number * generator, then gets the inet address of the server host, and adds each byte * of the address to the hash, and then appends the time, pid and a random * integer. */ char * auth_genchallenge(in_addr_t s_addr, const char *private_key) { MD5_CTX c; struct in_addr ia; char buf[1024]; char md5buf[MD5_DIGEST_SIZE]; char *addr, *challenge, *tok; int addr_byte, i; i = 0; ia.s_addr = s_addr; addr = inet_ntoa(ia); challenge = xmalloc(MD5_DIGEST_SIZE); srandom(time(0)); MD5_Init(&c); while ((tok = strsep(&addr, ".")) != NULL) { /* * XXX: Not sure if this should be checked, because one should expect * the s_addr to be a valid address. */ addr_byte = strtol(tok, NULL, 0); snprintf(buf, sizeof(buf), "%d", addr_byte); MD5_Update(&c, buf, strlen(buf)); if (i < 3) MD5_Update(&c, ".", 1); i++; } MD5_Update(&c, ":", 1); snprintf(buf, sizeof(buf), "%d", time(0)); MD5_Update(&c, buf, strlen(buf)); MD5_Update(&c, ":", 1); snprintf(buf, sizeof(buf), "%d", getpid()); MD5_Update(&c, buf, strlen(buf)); MD5_Update(&c, ":", 1); snprintf(buf, sizeof(buf), "%ld", random()); MD5_Update(&c, buf, strlen(buf)); MD5_Update(&c, ":", 1); MD5_Update(&c, private_key, strlen(private_key)); MD5_End(md5buf, &c); strncpy(challenge, md5buf, sizeof(challenge)-1); challenge[sizeof(challenge) - 1] = '\0'; return (challenge); } /* * Checks server response by generating it's own response to the challenge and * secret, and then comparing it to the response from the server. */ int auth_checkresponse(char *response, char *challenge, char *ssecret) { char *myresponse; myresponse = auth_genresponse(challenge, ssecret); if (strcmp(response, myresponse) == 0) { free(myresponse); return (1); } free(myresponse); return (0); } /* * Respond with correct to the server according to the protocol. */ void auth_respond(struct stream *s, struct auth_record *r, char *response, char *challenge) { proto_printf(s, "AUTHMD5 %S %S %S\n", r->client, response, challenge); stream_flush(s); } /* Convert buf to lowercase */ static void tolowercase(char *buf) { unsigned int i, size; size = strlen(buf); for (i = 0; i < size; i++) buf[i] = tolower(buf[i]); }