"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
Re: got-notify-http: implement basic auth
To:
Omar Polo <op@omarpolo.com>
Cc:
gameoftrees@openbsd.org
Date:
Tue, 16 Apr 2024 12:22:53 +0200

Download raw body.

Thread
On Tue, Apr 16, 2024 at 10:11:53AM +0200, Omar Polo wrote:
> On 2024/04/16 01:25:04 +0200, Omar Polo <op@omarpolo.com> wrote:
> > with this the authentication is properly implemented.  I'm unsure how to
> > plug it in the regress, so for the moment no tests.
> 
> here's an improved diff without extra include and with the regress too.
> It makes the log output more verbose as a drawback though:
> 
> : gotd: gotd: WARNING: Using basic authentication over plaintext http://
> : will leak credentials; https:// is recommended for URL
> : 'http://localhost:8000/'

Looks good to me, ok stsp@

One thing I don't quite understand is how our regress verifies
authentication. The HTTP server seems to compare the auth token
to a known base64 encoded value?  Could we use some Perl module
to check whether a real web server will accept the token we send?

> diff /home/op/w/got
> commit - c1003102a22a77d068a14f9ffa7877f67c28e95d
> path + /home/op/w/got
> blob - 9c8a90e392c27e03eb1dd32a88414aa62a355012
> file + gotd/libexec/got-notify-http/got-notify-http.c
> --- gotd/libexec/got-notify-http/got-notify-http.c
> +++ gotd/libexec/got-notify-http/got-notify-http.c
> @@ -730,17 +730,70 @@ jsonify(FILE *fp, const char *repo)
>  	return 0;
>  }
>  
> +static char
> +sixet2ch(int c)
> +{
> +	c &= 0x3F;
> +
> +	if (c < 26)
> +		return 'A' + c;
> +	c -= 26;
> +	if (c < 26)
> +		return 'a' + c;
> +	c -= 26;
> +	if (c < 10)
> +		return '0' + c;
> +	c -= 10;
> +	if (c == 0)
> +		return '+';
> +	if (c == 1)
> +		return '/';
> +
> +	errx(1, "invalid sixet 0x%x", c);
> +}
> +
>  static char *
>  basic_auth(const char *username, const char *password)
>  {
> -	char	*tmp;
> -	int	 len;
> +	char	*str, *tmp, *end, *s, *p;
> +	char	 buf[3];
> +	int	 len, i, r;
>  
> -	len = asprintf(&tmp, "%s:%s", username, password);
> -	if (len == -1)
> +	r = asprintf(&str, "%s:%s", username, password);
> +	if (r == -1)
>  		err(1, "asprintf");
>  
> -	/* XXX base64-ify */
> +	/*
> +	 * Will need 4 * r/3 bytes to encode the string, plus a
> +	 * rounding to the next multiple of 4 for padding, plus NUL.
> +	 */
> +	len = 4 * r / 3;
> +	len = (len + 3) & ~3;
> +	len++;
> +
> +	tmp = calloc(1, len);
> +	if (tmp == NULL)
> +		err(1, "malloc");
> +
> +	s = str;
> +	p = tmp;
> +	while (*s != '\0') {
> +		memset(buf, 0, sizeof(buf));
> +		for (i = 0; i < 3 && *s != '\0'; ++i, ++s)
> +			buf[i] = *s;
> +
> +		*p++ = sixet2ch(buf[0] >> 2);
> +		*p++ = sixet2ch((buf[1] >> 4) | (buf[0] << 4));
> +		if (i > 1)
> +			*p++ = sixet2ch((buf[1] << 2) | (buf[2] >> 6));
> +		if (i > 2)
> +			*p++ = sixet2ch(buf[2]);
> +	}
> +
> +	for (end = tmp + len - 1; p < end; ++p)
> +		*p = '=';
> +
> +	free(str);
>  	return tmp;
>  }
>  
> blob - 682368c09afd8f26371f3c0eb5b63f700ac39026
> file + gotd/notify.c
> --- gotd/notify.c
> +++ gotd/notify.c
> @@ -161,7 +161,8 @@ gotd_notify_sighdlr(int sig, short event, void *arg)
>  }
>  
>  static void
> -run_notification_helper(const char *prog, const char **argv, int fd)
> +run_notification_helper(const char *prog, const char **argv, int fd,
> +    const char *user, const char *pass)
>  {
>  	const struct got_error *err = NULL;
>  	pid_t pid;
> @@ -185,6 +186,11 @@ run_notification_helper(const char *prog, const char *
>  
>  		closefrom(STDERR_FILENO + 1);
>  
> +		if (user != NULL && pass != NULL) {
> +			setenv("GOT_NOTIFY_HTTP_USER", user, 1);
> +			setenv("GOT_NOTIFY_HTTP_PASS", pass, 1);
> +		}
> +
>  		if (execv(prog, (char *const *)argv) == -1) {
>  			fprintf(stderr, "%s: exec %s: %s\n", getprogname(),
>  			    prog, strerror(errno));
> @@ -249,7 +255,8 @@ notify_email(struct gotd_notification_target *target, 
>  
>  	argv[i] = NULL;
>  
> -	run_notification_helper(GOTD_PATH_PROG_NOTIFY_EMAIL, argv, fd);
> +	run_notification_helper(GOTD_PATH_PROG_NOTIFY_EMAIL, argv, fd,
> +	    NULL, NULL);
>  }
>  
>  static void
> @@ -273,7 +280,8 @@ notify_http(struct gotd_notification_target *target, c
>  
>  	argv[argc] = NULL;
>  
> -	run_notification_helper(GOTD_PATH_PROG_NOTIFY_HTTP, argv, fd);
> +	run_notification_helper(GOTD_PATH_PROG_NOTIFY_HTTP, argv, fd,
> +	    target->conf.http.user, target->conf.http.password);
>  }
>  
>  static const struct got_error *
> blob - 0dee7475233829d5d5fc2cc3f028cfc7789ce428
> file + regress/gotd/Makefile
> --- regress/gotd/Makefile
> +++ regress/gotd/Makefile
> @@ -188,7 +188,7 @@ start_gotd_http_notification: ensure_root
>  	@echo '    path "$(GOTD_TEST_REPO)"' >> $(PWD)/gotd.conf
>  	@echo '    permit rw $(GOTD_DEVUSER)' >> $(PWD)/gotd.conf
>  	@echo '    notify {' >> $(PWD)/gotd.conf
> -	@echo '         url "http://localhost:${GOTD_TEST_HTTP_PORT}/"' >> $(PWD)/gotd.conf
> +	@echo '         url "http://localhost:${GOTD_TEST_HTTP_PORT}/" user flan password "password"' >> $(PWD)/gotd.conf
>  	@echo "    }" >> $(PWD)/gotd.conf
>  	@echo "}" >> $(PWD)/gotd.conf
>  	@$(GOTD_TRAP); $(GOTD_START_CMD)
> blob - 5cbff3264bfe341856058856515d41c34ed996bf
> file + regress/gotd/http-server
> --- regress/gotd/http-server
> +++ regress/gotd/http-server
> @@ -18,10 +18,11 @@ use v5.36;
>  use IPC::Open2;
>  use Getopt::Long qw(:config bundling);
>  
> +my $auth;
>  my $port = 8000;
>  
> -GetOptions("p:i" => \$port)
> -    or die("usage: $0 [-p port]\n");
> +GetOptions("a:s" => \$auth, "p:i" => \$port)
> +    or die("usage: $0 [-a auth] [-p port]\n");
>  
>  my $pid = open2(my $out, my $in, 'nc', '-l', 'localhost', $port);
>  
> @@ -61,6 +62,15 @@ while (<$out>) {
>  		    unless m/Connection: close$/;
>  		next;
>  	}
> +
> +	if (m/Authorization/) {
> +		die "bad authorization header"
> +		    unless m/Authorization: basic (.*)$/;
> +		my $t = $1;
> +		die "wrong authorization; got $t want $auth"
> +		    if not defined($auth) or $auth ne $t;
> +		next;
> +	}
>  }
>  
>  die "no Content-Length header" unless defined $clen;
> blob - 38661743a189d324f50db59d65ebfb278ed84eff
> file + regress/gotd/http_notification.sh
> --- regress/gotd/http_notification.sh
> +++ regress/gotd/http_notification.sh
> @@ -17,6 +17,9 @@
>  . ../cmdline/common.sh
>  . ./common.sh
>  
> +# flan:password encoded in base64
> +AUTH="ZmxhbjpwYXNzd29yZA=="
> +
>  test_file_changed() {
>  	local testroot=`test_init file_changed 1`
>  
> @@ -41,7 +44,7 @@ test_file_changed() {
>  	local commit_id=`git_show_head $testroot/repo-clone`
>  	local author_time=`git_show_author_time $testroot/repo-clone`
>  
> -	timeout 5 ./http-server -p $GOTD_TEST_HTTP_PORT \
> +	timeout 5 ./http-server -a $AUTH -p $GOTD_TEST_HTTP_PORT \
>  	    > $testroot/stdout &
>  
>  	got send -b main -q -r $testroot/repo-clone
> @@ -134,7 +137,7 @@ test_bad_utf8() {
>  	local commit_id=`git_show_head $testroot/repo-clone`
>  	local author_time=`git_show_author_time $testroot/repo-clone`
>  
> -	timeout 5 ./http-server -p $GOTD_TEST_HTTP_PORT \
> +	timeout 5 ./http-server -a $AUTH -p $GOTD_TEST_HTTP_PORT \
>  	    > $testroot/stdout &
>  
>  	got send -b main -q -r $testroot/repo-clone
> @@ -229,7 +232,7 @@ test_many_commits_not_summarized() {
>  		set -- "$@" "$commit_id $d"
>  	done
>  
> -	timeout 5 ./http-server -p "$GOTD_TEST_HTTP_PORT" \
> +	timeout 5 ./http-server -a $AUTH -p "$GOTD_TEST_HTTP_PORT" \
>  	    > $testroot/stdout &
>  
>  	got send -b main -q -r $testroot/repo-clone
> @@ -334,7 +337,7 @@ test_many_commits_summarized() {
>  		set -- "$@" "$short_commit_id $d"
>  	done
>  
> -	timeout 5 ./http-server -p "$GOTD_TEST_HTTP_PORT" \
> +	timeout 5 ./http-server -a $AUTH -p "$GOTD_TEST_HTTP_PORT" \
>  	    > $testroot/stdout &
>  
>  	got send -b main -q -r $testroot/repo-clone
> @@ -414,7 +417,7 @@ test_branch_created() {
>  	local commit_id=`git_show_branch_head $testroot/repo-clone newbranch`
>  	local author_time=`git_show_author_time $testroot/repo-clone $commit_id`
>  
> -	timeout 5 ./http-server -p "$GOTD_TEST_HTTP_PORT" \
> +	timeout 5 ./http-server -a $AUTH -p "$GOTD_TEST_HTTP_PORT" \
>  	    > $testroot/stdout &
>  
>  	got send -b newbranch -q -r $testroot/repo-clone
> @@ -501,7 +504,7 @@ test_branch_removed() {
>  		return 1
>  	fi
>  
> -	timeout 5 ./http-server -p "$GOTD_TEST_HTTP_PORT" \
> +	timeout 5 ./http-server -a $AUTH -p "$GOTD_TEST_HTTP_PORT" \
>  	    > $testroot/stdout &
>  
>  	local commit_id=`git_show_branch_head $testroot/repo-clone newbranch`
> @@ -556,7 +559,7 @@ test_tag_created() {
>  	local commit_id=`git_show_head $testroot/repo-clone`
>  	local tagger_time=`git_show_tagger_time $testroot/repo-clone 1.0`
>  
> -	timeout 5 ./http-server -p "$GOTD_TEST_HTTP_PORT" \
> +	timeout 5 ./http-server -a $AUTH -p "$GOTD_TEST_HTTP_PORT" \
>  	    >$testroot/stdout &
>  
>  	got send -t 1.0 -q -r $testroot/repo-clone
> @@ -634,7 +637,7 @@ test_tag_changed() {
>  	got tag -r $testroot/repo-clone -m "new tag" 1.0 > /dev/null
>  	local tagger_time=`git_show_tagger_time $testroot/repo-clone 1.0`
>  
> -	timeout 5 ./http-server -p "$GOTD_TEST_HTTP_PORT" \
> +	timeout 5 ./http-server -a $AUTH -p "$GOTD_TEST_HTTP_PORT" \
>  	    > $testroot/stdout &
>  
>  	got send -f -t 1.0 -q -r $testroot/repo-clone
> 
>