Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 2 Feb 2010 05:57:42 +0000 (UTC)
From:      Ulf Lilleengen <lulf@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r203368 - in head: contrib/csup usr.bin/csup
Message-ID:  <201002020557.o125vgTl089837@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: lulf
Date: Tue Feb  2 05:57:42 2010
New Revision: 203368
URL: http://svn.freebsd.org/changeset/base/203368

Log:
  - Add support for CVSup authentication mechanisms to csup.
  - Include a cpasswd script performing the same mechanisms as the cvpasswd
    utility from CVSup.
  
  PR:		bin/114129
  Submitted by:	Petar Zhivkov Petrov <pesho.petrov -at- gmail.com>
  MFC after:	1 month

Added:
  head/contrib/csup/auth.c   (contents, props changed)
  head/contrib/csup/auth.h   (contents, props changed)
  head/contrib/csup/cpasswd.1
  head/contrib/csup/cpasswd.sh   (contents, props changed)
Modified:
  head/contrib/csup/Makefile
  head/contrib/csup/TODO
  head/contrib/csup/config.h
  head/contrib/csup/csup.1
  head/contrib/csup/main.c
  head/contrib/csup/proto.c
  head/usr.bin/csup/Makefile

Modified: head/contrib/csup/Makefile
==============================================================================
--- head/contrib/csup/Makefile	Tue Feb  2 01:20:33 2010	(r203367)
+++ head/contrib/csup/Makefile	Tue Feb  2 05:57:42 2010	(r203368)
@@ -7,7 +7,7 @@ MANDIR?=	${PREFIX}/man/man
 UNAME!=		/usr/bin/uname -s
 
 PROG=	csup
-SRCS=	attrstack.c config.c detailer.c diff.c fattr.c fixups.c fnmatch.c \
+SRCS=	attrstack.c auth.c config.c detailer.c diff.c fattr.c fixups.c fnmatch.c \
 	globtree.c idcache.c keyword.c lister.c main.c misc.c mux.c parse.y \
 	pathcomp.c proto.c status.c stream.c threads.c token.l updater.c \
 	rcsfile.c rcsparse.c lex.rcs.c rsyncfile.c
@@ -42,4 +42,7 @@ parse.h:	y.tab.h
 DPADD=	${LIBCRYPTO} ${LIBZ}
 LDADD=	-lcrypto -lz
 
+SCRIPTS=	cpasswd.sh
+MAN=		csup.1 cpasswd.1
+
 .include <bsd.prog.mk>

Modified: head/contrib/csup/TODO
==============================================================================
--- head/contrib/csup/TODO	Tue Feb  2 01:20:33 2010	(r203367)
+++ head/contrib/csup/TODO	Tue Feb  2 05:57:42 2010	(r203368)
@@ -17,7 +17,6 @@ BUGS:
 
 MISSING FEATURES:
 
-- Add support for authentication.
 - Add support for shell commands sent by the server.
 - Add missing support for various CVSup options : -D, -a (requires
   authentication support), -e and -E (requires shell commands support)

Added: head/contrib/csup/auth.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/contrib/csup/auth.c	Tue Feb  2 05:57:42 2010	(r203368)
@@ -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]);
+	}
+}
+

Added: head/contrib/csup/auth.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/contrib/csup/auth.h	Tue Feb  2 05:57:42 2010	(r203368)
@@ -0,0 +1,38 @@
+/*-
+ * 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$
+ */
+#ifndef _AUTH_H_
+#define _AUTH_H_
+
+#define	AUTHFILE	".csup/auth" /* user home relative */
+
+struct config;
+
+int auth_login(struct config *);
+
+#endif /* !_AUTH_H_ */
+

Modified: head/contrib/csup/config.h
==============================================================================
--- head/contrib/csup/config.h	Tue Feb  2 01:20:33 2010	(r203367)
+++ head/contrib/csup/config.h	Tue Feb  2 05:57:42 2010	(r203368)
@@ -108,6 +108,7 @@ struct config {
 	struct chan *chan1;
 	struct stream *server;
 	fattr_support_t fasupport;
+	int reqauth;
 };
 
 struct config	*config_init(const char *, struct coll *, int);

Added: head/contrib/csup/cpasswd.1
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/contrib/csup/cpasswd.1	Tue Feb  2 05:57:42 2010	(r203368)
@@ -0,0 +1,120 @@
+.\" Copyright 1999-2003 John D. Polstra.
+.\" 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgment:
+.\"      This product includes software developed by John D. Polstra.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\"    derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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: cvpasswd.1,v 1.4 2003/03/04 18:24:42 jdp Exp $
+.\" $FreeBSD $
+.\"
+.Dd June 27, 2007
+.Os FreeBSD
+.Dt CPASSWD 1
+.Sh NAME
+.Nm cpasswd
+.Nd scramble passwords for csup authentication
+.Sh SYNOPSIS
+.Nm
+.Ar clientName
+.Ar serverName
+.Sh DESCRIPTION
+The
+.Nm
+utility creates scrambled passwords for the
+.Nm CVSup
+server's authentication database.  It is invoked with a client name
+and a server name.
+.Ar ClientName
+is the name the client uses to gain access to the
+server.  By convention, e-mail addresses are used for all client
+names, e.g.,
+.Ql BillyJoe@FreeBSD.ORG .
+Client names are case-insensitive.
+.Pp 
+.Ar ServerName
+is the name of the
+.Nm CVSup
+server which the client wishes to access.  By convention,
+it is the canonical fully-qualified domain name of the server, e.g.,
+.Ql CVSup.FreeBSD.ORG .
+This must agree with the server's own idea of its name.  The name is
+case-insensitive.
+.Pp
+To set up authentication for a given server, one must perform the
+following steps:
+.Bl -enum
+.It
+Obtain the official
+.Ar serverName
+from the administrator of the server or from some other source.
+.It
+Choose an appropriate
+.Ar clientName .
+It should be in the form of a valid e-mail address, to make it easy
+for the server administrator to contact the user if necessary.
+.It
+Choose an arbitrary secret
+.Ar password .
+.It
+Run
+.Nm cpasswd ,
+and type in the
+.Ar password
+when prompted for it.  The utility will print out a line to send
+to the server administrator, and instruct you how to modify your
+.Li $ Ns Ev HOME Ns Pa /.csup/auth
+file.  You should use a secure channel to send the line to the
+server administrator.
+.El
+.Pp
+Since
+.Li $ Ns Ev HOME Ns Pa /.csup/auth
+contains passwords, you should ensure that it is not readable by
+anyone except yourself.
+.Sh FILES
+.Bl -tag -width $HOME/.csup/authxx -compact
+.It Li $ Ns Ev HOME Ns Pa /.csup/auth
+Authentication password file.
+.El
+.Sh SEE ALSO
+.Xr csup 1 ,
+.Xr cvsup 1 ,
+.Xr cvsupd 8 .
+.Pp
+.Bd -literal
+http://www.cvsup.org/
+.Ed
+.Sh AUTHORS
+.An -nosplit
+.An Petar Zhivkov Petrov Aq pesho.petrov@gmail.com
+is the author of
+.Nm ,
+the rewrite of
+.Nm cvpasswd .
+.An John Polstra Aq jdp@polstra.com
+is the author of
+.Nm CVSup .
+.Sh LEGALITIES
+CVSup is a registered trademark of John D. Polstra.

Added: head/contrib/csup/cpasswd.sh
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/contrib/csup/cpasswd.sh	Tue Feb  2 05:57:42 2010	(r203368)
@@ -0,0 +1,135 @@
+#! /bin/sh
+#
+# Copyright 2007. Petar Zhivkov Petrov 
+# pesho.petrov@gmail.com
+#
+# $FreeBSD$
+
+usage() {
+	echo "Usage: $0 clientName serverName"
+	echo "       $0 -v"
+}
+
+countChars() {
+    _count="`echo "$1" | sed -e "s/[^$2]//g" | tr -d "\n" | wc -c`"
+	return 0
+}
+
+readPassword() {
+	while [ true ]; do
+		stty -echo
+		read -p "$1" _password
+		stty echo
+		echo ""
+		countChars "$_password" ":"
+		if [ $_count != 0 ]; then
+			echo "Sorry, password must not contain \":\" characters"
+			echo ""
+		else
+			break
+		fi
+	done
+	return 0
+}
+
+makeSecret() {
+	local clientLower="`echo "$1" | tr "[:upper:]" "[:lower:]"`"
+	local serverLower="`echo "$2" | tr "[:upper:]" "[:lower:]"`"
+	local secret="`md5 -qs "$clientLower:$serverLower:$3"`"
+	_secret="\$md5\$$secret"
+}
+
+if [ $# -eq 1 -a "X$1" = "X-v" ]; then
+	echo "Csup authentication key generator"
+	usage
+	exit
+elif [ $# -ne 2 ]; then
+	usage
+	exit
+fi
+
+clientName=$1
+serverName=$2
+
+#
+# Client name must contain exactly one '@' and at least one '.'.
+# It must not contain a ':'.
+#
+
+countChars "$clientName" "@"
+aCount=$_count
+
+countChars "$clientName" "."
+dotCount=$_count
+if [ $aCount -ne 1 -o $dotCount -eq 0 ]; then
+	echo "Client name must have the form of an e-mail address,"
+	echo "e.g., \"user@domain.com\""
+	exit
+fi
+
+countChars "$clientName" ":"
+colonCount=$_count
+if [ $colonCount -gt 0 ]; then
+	echo "Client name must not contain \":\" characters"
+	exit
+fi
+
+#
+# Server name must not contain '@' and must have at least one '.'.
+# It also must not contain a ':'.
+#
+
+countChars "$serverName" "@"
+aCount=$_count
+
+countChars "$serverName" "."
+dotCount=$_count
+if [ $aCount != 0 -o $dotCount = 0 ]; then
+	echo "Server name must be a fully-qualified domain name."
+	echo "e.g., \"host.domain.com\""
+	exit
+fi
+
+countChars "$serverName" ":"
+colonCount=$_count
+if [ $colonCount -gt 0 ]; then
+	echo "Server name must not contain \":\" characters"
+	exit
+fi
+
+#
+# Ask for password and generate secret.
+#
+
+while [ true ]; do
+	readPassword "Enter password: "
+	makeSecret "$clientName" "$serverName" "$_password"
+	secret=$_secret
+
+	readPassword "Enter same password again: "
+	makeSecret "$clientName" "$serverName" "$_password"
+	secret2=$_secret
+
+	if [ "X$secret" = "X$secret2" ]; then
+		break
+	else
+		echo "Passwords did not match.  Try again."
+		echo ""
+	fi
+done
+
+echo ""
+echo "Send this line to the server administrator at $serverName:"
+echo "-------------------------------------------------------------------------------"
+echo "$clientName:$secret::"
+echo "-------------------------------------------------------------------------------"
+echo "Be sure to send it using a secure channel!"
+echo ""
+echo "Add this line to your file \"$HOME/.csup/auth\", replacing \"XXX\""
+echo "with the password you typed in:"
+echo "-------------------------------------------------------------------------------"
+echo "$serverName:$clientName:XXX:"
+echo "-------------------------------------------------------------------------------"
+echo "Make sure the file is readable and writable only by you!"
+echo ""
+

Modified: head/contrib/csup/csup.1
==============================================================================
--- head/contrib/csup/csup.1	Tue Feb  2 01:20:33 2010	(r203367)
+++ head/contrib/csup/csup.1	Tue Feb  2 05:57:42 2010	(r203368)
@@ -32,7 +32,7 @@
 .Nd network distribution package for CVS repositories
 .Sh SYNOPSIS
 .Nm
-.Op Fl 146ksvzZ
+.Op Fl 146aksvzZ
 .Op Fl A Ar addr
 .Op Fl b Ar base
 .Op Fl c Ar collDir
@@ -106,6 +106,12 @@ to use IPv4 addresses only.
 Forces
 .Nm
 to use IPv6 addresses only.
+.It Fl a
+Requires the server to authenticate itself (prove its identity) to
+the client.  If authentication of the server fails, the update is
+canceled.  See
+.Sx AUTHENTICATION ,
+below.
 .It Fl A Ar addr
 Specifies a local address to bind to when connecting to the server.
 The local address might be a hostname or a numeric host address string
@@ -793,6 +799,102 @@ as well:
 .It
 .Pa /bar/stool/src-all/refuse.cvs:RELENG_3
 .El
+.Sh AUTHENTICATION
+.Nm
+implements an optional authentication mechanism which can be used by the
+client and server to verify each other's identities.
+Public CVSup servers normally do not enable authentication.
+.Nm
+users may ignore this section unless they have been informed
+that authentication is required by the administrator of their server.
+.Pp
+The authentication subsystem uses a
+challenge-response protocol which is immune to packet sniffing and
+replay attacks.  No passwords are sent over the network in either
+direction.  Both the client and the server can independently verify
+the identities of each other.
+.Pp
+The file
+.Li $ Ns Ev HOME Ns Pa /.csup/auth
+holds the information used for authentication.  This file contains a
+record for each server that the client is allowed to access.  Each
+record occupies one line in the file.  Lines beginning with
+.Ql #
+are ignored, as are lines containing only white space.  White space is
+significant everywhere else in the file.  Fields are separated by
+.Ql \&:
+characters.
+.Pp
+Each record of the file has the following form:
+.Bd -literal -offset indent
+.Sm off
+.Xo Ar serverName No : Ar clientName No :
+.Ar password No : Ar comment
+.Xc
+.Sm on
+.Ed
+.Pp
+All fields must be present even if some of them are empty.
+.Ar ServerName
+is the name of the server to which the record applies.  By convention,
+it is the canonical fully-qualified domain name of the server, e.g.,
+.Ql CVSup177.FreeBSD.ORG .
+This must agree with the server's own idea of its name.  The name is
+case-insensitive.
+.Pp
+.Ar ClientName
+is the name the client uses to gain access to the server.  By
+convention, e-mail addresses are used for all client names, e.g.,
+.Ql BillyJoe@FreeBSD.ORG .
+Client names are case-insensitive.
+.Pp
+.Ar Password
+is a secret string of characters that the client uses to prove its
+identity.  It may not contain any
+.Ql \&:
+or newline characters.
+.Pp
+.Ar Comment
+may contain any additional information to identify the record.  It
+is not interpreted by the program.
+.Pp
+To set up authentication for a given server, one must perform the
+following steps:
+.Bl -enum
+.It
+Obtain the official
+.Ar serverName
+from the administrator of the server or from some other source.
+.It
+Choose an appropriate
+.Ar clientName .
+It should be in the form of a valid e-mail address, to make it easy
+for the server administrator to contact the user if necessary.
+.It
+Choose an arbitrary secret
+.Ar password .
+.It
+Run the
+.Nm cpasswd
+utility, and type in the
+.Ar password
+when prompted for it.  The utility will print out a line to send
+to the server administrator, and instruct you how to modify your
+.Li $ Ns Ev HOME Ns Pa /.csup/auth
+file.  You should use a secure channel to send the line to the
+server administrator.
+.El
+.Pp
+Since
+.Li $ Ns Ev HOME Ns Pa /.csup/auth
+contains passwords, you should ensure that it is not readable by
+anyone except yourself.
+.Pp
+Authentication works independently in both directions.  The server
+administrator controls whether you must prove your identity.
+You control whether to check the server's identity, by means of the
+.Fl a
+command line option.
 .Sh csup AND FIREWALLS
 In its default mode,
 .Nm
@@ -865,6 +967,7 @@ subdirectory.
 List files.
 .El
 .Sh SEE ALSO
+.Xr cpasswd 1 ,
 .Xr cvs 1 ,
 .Xr rcsintro 1 ,
 .Xr ssh 1 .

Modified: head/contrib/csup/main.c
==============================================================================
--- head/contrib/csup/main.c	Tue Feb  2 01:20:33 2010	(r203367)
+++ head/contrib/csup/main.c	Tue Feb  2 05:57:42 2010	(r203368)
@@ -60,6 +60,8 @@ usage(char *argv0)
 	    "(same as \"-r 0\")");
 	lprintf(-1, USAGE_OPTFMT, "-4", "Force usage of IPv4 addresses");
 	lprintf(-1, USAGE_OPTFMT, "-6", "Force usage of IPv6 addresses");
+	lprintf(-1, USAGE_OPTFMT, "-a",
+		"Require server to authenticate itself to us");
 	lprintf(-1, USAGE_OPTFMT, "-A addr",
 	    "Bind local socket to a specific address");
 	lprintf(-1, USAGE_OPTFMT, "-b base",
@@ -107,7 +109,7 @@ main(int argc, char *argv[])
 	struct stream *lock;
 	char *argv0, *file, *lockfile;
 	int family, error, lockfd, lflag, overridemask;
-	int c, i, deletelim, port, retries, status;
+	int c, i, deletelim, port, retries, status, reqauth;
 	time_t nexttry;
 
 	error = 0;
@@ -124,9 +126,10 @@ main(int argc, char *argv[])
 	lockfile = NULL;
 	override = coll_new(NULL);
 	overridemask = 0;
+	reqauth = 0;
 
 	while ((c = getopt(argc, argv,
-	    "146A:b:c:d:gh:i:kl:L:p:P:r:svzZ")) != -1) {
+	    "146aA:b:c:d:gh:i:kl:L:p:P:r:svzZ")) != -1) {
 		switch (c) {
 		case '1':
 			retries = 0;
@@ -137,6 +140,10 @@ main(int argc, char *argv[])
 		case '6':
 			family = AF_INET6;
 			break;
+		case 'a':
+			/* Require server authentication */
+			reqauth = 1;
+			break;
 		case 'A':
 			error = getaddrinfo(optarg, NULL, NULL, &res);
 			if (error) {
@@ -303,6 +310,7 @@ main(int argc, char *argv[])
 		config->laddrlen = laddrlen;
 	}
 	config->deletelim = deletelim;
+	config->reqauth = reqauth;
 	lprintf(2, "Connecting to %s\n", config->host);
 
 	i = 0;

Modified: head/contrib/csup/proto.c
==============================================================================
--- head/contrib/csup/proto.c	Tue Feb  2 01:20:33 2010	(r203367)
+++ head/contrib/csup/proto.c	Tue Feb  2 05:57:42 2010	(r203368)
@@ -45,6 +45,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "auth.h"
 #include "config.h"
 #include "detailer.h"
 #include "fattr.h"
@@ -74,7 +75,6 @@ static void		 killer_stop(struct killer 
 static int		 proto_waitconnect(int);
 static int		 proto_greet(struct config *);
 static int		 proto_negproto(struct config *);
-static int		 proto_login(struct config *);
 static int		 proto_fileattr(struct config *);
 static int		 proto_xchgcoll(struct config *);
 static struct mux	*proto_mux(struct config *);
@@ -251,56 +251,6 @@ bad:
 	return (STATUS_FAILURE);
 }
 
-static int
-proto_login(struct config *config)
-{
-	struct stream *s;
-	char hostbuf[MAXHOSTNAMELEN];
-	char *line, *login, *host, *cmd, *realm, *challenge, *msg;
-	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);
-	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)
-		goto bad;
-	if (strcmp(realm, ".") != 0 || strcmp(challenge, ".") != 0) {
-		lprintf(-1, "Authentication required by the server and not "
-		    "supported by client\n");
-		return (STATUS_FAILURE);
-	}
-	proto_printf(s, "AUTHMD5 . . .\n");
-	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)
-		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);
-}
-
 /*
  * File attribute support negotiation.
  */
@@ -601,7 +551,7 @@ proto_run(struct config *config)
 	if (status == STATUS_SUCCESS)
 		status = proto_negproto(config);
 	if (status == STATUS_SUCCESS)
-		status = proto_login(config);
+		status = auth_login(config);
 	if (status == STATUS_SUCCESS)
 		status = proto_fileattr(config);
 	if (status == STATUS_SUCCESS)

Modified: head/usr.bin/csup/Makefile
==============================================================================
--- head/usr.bin/csup/Makefile	Tue Feb  2 01:20:33 2010	(r203367)
+++ head/usr.bin/csup/Makefile	Tue Feb  2 05:57:42 2010	(r203368)
@@ -4,6 +4,7 @@
 
 PROG=	csup
 SRCS=	attrstack.c \
+	auth.c \
 	config.c \
 	detailer.c \
 	diff.c \
@@ -37,4 +38,7 @@ WARNS?=	1
 DPADD=	${LIBCRYPTO} ${LIBZ} ${LIBPTHREAD}
 LDADD=	-lcrypto -lz -lpthread
 
+SCRIPTS=	cpasswd.sh
+MAN=		csup.1 cpasswd.1
+
 .include <bsd.prog.mk>



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201002020557.o125vgTl089837>