summaryrefslogtreecommitdiffstats
path: root/contrib/csup/auth.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/csup/auth.c')
-rw-r--r--contrib/csup/auth.c331
1 files changed, 331 insertions, 0 deletions
diff --git a/contrib/csup/auth.c b/contrib/csup/auth.c
new file mode 100644
index 0000000..4e79bc5
--- /dev/null
+++ b/contrib/csup/auth.c
@@ -0,0 +1,331 @@
+/*-
+ * Copyright (c) 2003-2007, Petar Zhivkov Petrov <pesho.petrov@gmail.com>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <openssl/md5.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "auth.h"
+#include "config.h"
+#include "misc.h"
+#include "proto.h"
+#include "stream.h"
+
+#define MD5_BYTES 16
+
+/* This should be at least 2 * MD5_BYTES + 6 (length of "$md5$" + 1) */
+#define MD5_CHARS_MAX (2*(MD5_BYTES)+6)
+
+struct srvrecord {
+ char server[MAXHOSTNAMELEN];
+ char client[256];
+ char password[256];
+};
+
+static int auth_domd5auth(struct config *);
+static int auth_lookuprecord(char *, struct srvrecord *);
+static int auth_parsetoken(char **, char *, int);
+static void auth_makesecret(struct srvrecord *, char *);
+static void auth_makeresponse(char *, char *, char *);
+static void auth_readablesum(unsigned char *, char *);
+static void auth_makechallenge(struct config *, char *);
+static int auth_checkresponse(char *, char *, char *);
+
+int auth_login(struct config *config)
+{
+ struct stream *s;
+ char hostbuf[MAXHOSTNAMELEN];
+ char *login, *host;
+ int error;
+
+ s = config->server;
+ error = gethostname(hostbuf, sizeof(hostbuf));
+ hostbuf[sizeof(hostbuf) - 1] = '\0';
+ if (error)
+ host = NULL;
+ else
+ host = hostbuf;
+ login = getlogin();
+ proto_printf(s, "USER %s %s\n", login != NULL ? login : "?",
+ host != NULL ? host : "?");
+ stream_flush(s);
+ error = auth_domd5auth(config);
+ return (error);
+}
+
+static int
+auth_domd5auth(struct config *config)
+{
+ struct stream *s;
+ char *line, *cmd, *challenge, *realm, *client, *srvresponse, *msg;
+ char shrdsecret[MD5_CHARS_MAX], response[MD5_CHARS_MAX];
+ char clichallenge[MD5_CHARS_MAX];
+ struct srvrecord auth;
+ int error;
+
+ lprintf(2, "MD5 authentication started\n");
+ s = config->server;
+ line = stream_getln(s, NULL);
+ cmd = proto_get_ascii(&line);
+ realm = proto_get_ascii(&line);
+ challenge = proto_get_ascii(&line);
+ if (challenge == NULL ||
+ line != NULL ||
+ (strcmp(cmd, "AUTHMD5") != 0)) {
+ lprintf(-1, "Invalid server reply to USER\n");
+ return (STATUS_FAILURE);
+ }
+
+ client = NULL;
+ response[0] = clichallenge[0] = '.';
+ response[1] = clichallenge[1] = 0;
+ if (config->reqauth || (strcmp(challenge, ".") != 0)) {
+ if (strcmp(realm, ".") == 0) {
+ lprintf(-1, "Authentication required, but not enabled on server\n");
+ return (STATUS_FAILURE);
+ }
+ error = auth_lookuprecord(realm, &auth);
+ if (error != STATUS_SUCCESS)
+ return (error);
+ client = auth.client;
+ auth_makesecret(&auth, shrdsecret);
+ }
+
+ if (strcmp(challenge, ".") != 0)
+ auth_makeresponse(challenge, shrdsecret, response);
+ if (config->reqauth)
+ auth_makechallenge(config, clichallenge);
+ proto_printf(s, "AUTHMD5 %s %s %s\n",
+ client == NULL ? "." : client, response, clichallenge);
+ stream_flush(s);
+ line = stream_getln(s, NULL);
+ cmd = proto_get_ascii(&line);
+ if (cmd == NULL || line == NULL)
+ goto bad;
+ if (strcmp(cmd, "OK") == 0) {
+ srvresponse = proto_get_ascii(&line);
+ if (srvresponse == NULL)
+ goto bad;
+ if (config->reqauth &&
+ !auth_checkresponse(srvresponse, clichallenge, shrdsecret)) {
+ lprintf(-1, "Server failed to authenticate itself to client\n");
+ return (STATUS_FAILURE);
+ }
+ lprintf(2, "MD5 authentication successfull\n");
+ return (STATUS_SUCCESS);
+ }
+ if (strcmp(cmd, "!") == 0) {
+ msg = proto_get_rest(&line);
+ if (msg == NULL)
+ goto bad;
+ lprintf(-1, "Server error: %s\n", msg);
+ return (STATUS_FAILURE);
+ }
+bad:
+ lprintf(-1, "Invalid server reply to AUTHMD5\n");
+ return (STATUS_FAILURE);
+}
+
+static int
+auth_lookuprecord(char *server, struct srvrecord *auth)
+{
+ char *home, *line, authfile[FILENAME_MAX];
+ struct stream *s;
+ int linenum = 0, error;
+
+ home = getenv("HOME");
+ if (home == NULL) {
+ lprintf(-1, "Environment variable \"HOME\" is not set\n");
+ return (STATUS_FAILURE);
+ }
+ snprintf(authfile, sizeof(authfile), "%s/%s", home, AUTHFILE);
+ s = stream_open_file(authfile, O_RDONLY);
+ if (s == NULL) {
+ lprintf(-1, "Could not open file %s\n", authfile);
+ return (STATUS_FAILURE);
+ }
+
+ while ((line = stream_getln(s, NULL)) != NULL) {
+ linenum++;
+ if (line[0] == '#' || line[0] == '\0')
+ continue;
+ error = auth_parsetoken(&line, auth->server,
+ sizeof(auth->server));
+ if (error != STATUS_SUCCESS) {
+ lprintf(-1, "%s:%d Missng client name\n", authfile, linenum);
+ goto close;
+ }
+ /* Skip the rest of this line, it isn't what we are looking for. */
+ if (strcmp(auth->server, server) != 0)
+ continue;
+ error = auth_parsetoken(&line, auth->client,
+ sizeof(auth->client));
+ if (error != STATUS_SUCCESS) {
+ lprintf(-1, "%s:%d Missng password\n", authfile, linenum);
+ goto close;
+ }
+ error = auth_parsetoken(&line, auth->password,
+ sizeof(auth->password));
+ if (error != STATUS_SUCCESS) {
+ lprintf(-1, "%s:%d Missng comment\n", authfile, linenum);
+ goto close;
+ }
+ stream_close(s);
+ lprintf(2, "Found authentication record for server \"%s\"\n",
+ server);
+ return (STATUS_SUCCESS);
+ }
+ lprintf(-1, "Unknown server \"%s\". Fix your %s\n", server , authfile);
+ memset(auth->password, 0, sizeof(auth->password));
+close:
+ stream_close(s);
+ return (STATUS_FAILURE);
+}
+
+static int
+auth_parsetoken(char **line, char *buf, int len)
+{
+ char *colon;
+
+ colon = strchr(*line, ':');
+ if (colon == NULL)
+ return (STATUS_FAILURE);
+ *colon = 0;
+ buf[len - 1] = 0;
+ strncpy(buf, *line, len - 1);
+ *line = colon + 1;
+ return (STATUS_SUCCESS);
+}
+
+static void
+auth_makesecret(struct srvrecord *auth, char *secret)
+{
+ char *s, ch;
+ const char *md5salt = "$md5$";
+ unsigned char md5sum[MD5_BYTES];
+ MD5_CTX md5;
+
+ MD5_Init(&md5);
+ for (s = auth->client; *s != 0; ++s) {
+ ch = tolower(*s);
+ MD5_Update(&md5, &ch, 1);
+ }
+ MD5_Update(&md5, ":", 1);
+ for (s = auth->server; *s != 0; ++s) {
+ ch = tolower(*s);
+ MD5_Update(&md5, &ch, 1);
+ }
+ MD5_Update(&md5, ":", 1);
+ MD5_Update(&md5, auth->password, strlen(auth->password));
+ MD5_Final(md5sum, &md5);
+ memset(secret, 0, sizeof(secret));
+ strcpy(secret, md5salt);
+ auth_readablesum(md5sum, secret + strlen(md5salt));
+}
+
+static void
+auth_makeresponse(char *challenge, char *sharedsecret, char *response)
+{
+ MD5_CTX md5;
+ unsigned char md5sum[MD5_BYTES];
+
+ MD5_Init(&md5);
+ MD5_Update(&md5, sharedsecret, strlen(sharedsecret));
+ MD5_Update(&md5, ":", 1);
+ MD5_Update(&md5, challenge, strlen(challenge));
+ MD5_Final(md5sum, &md5);
+ auth_readablesum(md5sum, response);
+}
+
+/*
+ * Generates a challenge string which is an MD5 sum
+ * of a fairly random string. The purpose is to decrease
+ * the possibility of generating the same challenge
+ * string (even by different clients) more then once
+ * for the same server.
+ */
+static void
+auth_makechallenge(struct config *config, char *challenge)
+{
+ MD5_CTX md5;
+ unsigned char md5sum[MD5_BYTES];
+ char buf[128];
+ struct timeval tv;
+ struct sockaddr_in laddr;
+ pid_t pid, ppid;
+ int error, addrlen;
+
+ gettimeofday(&tv, NULL);
+ pid = getpid();
+ ppid = getppid();
+ srand(tv.tv_usec ^ tv.tv_sec ^ pid);
+ addrlen = sizeof(laddr);
+ error = getsockname(config->socket, (struct sockaddr *)&laddr, &addrlen);
+ if (error < 0) {
+ memset(&laddr, 0, sizeof(laddr));
+ }
+ gettimeofday(&tv, NULL);
+ MD5_Init(&md5);
+ snprintf(buf, sizeof(buf), "%s:%ld:%ld:%ld:%d:%d",
+ inet_ntoa(laddr.sin_addr), tv.tv_sec, tv.tv_usec, random(), pid, ppid);
+ MD5_Update(&md5, buf, strlen(buf));
+ MD5_Final(md5sum, &md5);
+ auth_readablesum(md5sum, challenge);
+}
+
+static int
+auth_checkresponse(char *response, char *challenge, char *secret)
+{
+ char correctresponse[MD5_CHARS_MAX];
+
+ auth_makeresponse(challenge, secret, correctresponse);
+ return (strcmp(response, correctresponse) == 0);
+}
+
+static void
+auth_readablesum(unsigned char *md5sum, char *readable)
+{
+ unsigned int i;
+ char *s = readable;
+
+ for (i = 0; i < MD5_BYTES; ++i, s+=2) {
+ sprintf(s, "%.2x", md5sum[i]);
+ }
+}
+
OpenPOWER on IntegriCloud