diff options
Diffstat (limited to 'contrib/csup')
-rw-r--r-- | contrib/csup/Makefile | 5 | ||||
-rw-r--r-- | contrib/csup/TODO | 1 | ||||
-rw-r--r-- | contrib/csup/auth.c | 331 | ||||
-rw-r--r-- | contrib/csup/auth.h | 38 | ||||
-rw-r--r-- | contrib/csup/config.h | 1 | ||||
-rw-r--r-- | contrib/csup/cpasswd.1 | 120 | ||||
-rwxr-xr-x | contrib/csup/cpasswd.sh | 135 | ||||
-rw-r--r-- | contrib/csup/csup.1 | 105 | ||||
-rw-r--r-- | contrib/csup/main.c | 12 | ||||
-rw-r--r-- | contrib/csup/proto.c | 54 |
10 files changed, 745 insertions, 57 deletions
diff --git a/contrib/csup/Makefile b/contrib/csup/Makefile index d82a0f1..ae34e75 100644 --- a/contrib/csup/Makefile +++ b/contrib/csup/Makefile @@ -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> diff --git a/contrib/csup/TODO b/contrib/csup/TODO index 41b2408..f4f0b31 100644 --- a/contrib/csup/TODO +++ b/contrib/csup/TODO @@ -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) 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]); + } +} + diff --git a/contrib/csup/auth.h b/contrib/csup/auth.h new file mode 100644 index 0000000..61052e3 --- /dev/null +++ b/contrib/csup/auth.h @@ -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_ */ + diff --git a/contrib/csup/config.h b/contrib/csup/config.h index 78b6995..859013c 100644 --- a/contrib/csup/config.h +++ b/contrib/csup/config.h @@ -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); diff --git a/contrib/csup/cpasswd.1 b/contrib/csup/cpasswd.1 new file mode 100644 index 0000000..946783f --- /dev/null +++ b/contrib/csup/cpasswd.1 @@ -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. diff --git a/contrib/csup/cpasswd.sh b/contrib/csup/cpasswd.sh new file mode 100755 index 0000000..71e17c5 --- /dev/null +++ b/contrib/csup/cpasswd.sh @@ -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 "" + diff --git a/contrib/csup/csup.1 b/contrib/csup/csup.1 index 4d22fc4..2690863 100644 --- a/contrib/csup/csup.1 +++ b/contrib/csup/csup.1 @@ -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 . diff --git a/contrib/csup/main.c b/contrib/csup/main.c index 7415039..e7711b3 100644 --- a/contrib/csup/main.c +++ b/contrib/csup/main.c @@ -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; diff --git a/contrib/csup/proto.c b/contrib/csup/proto.c index 15c1235..166a134 100644 --- a/contrib/csup/proto.c +++ b/contrib/csup/proto.c @@ -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) |