Initial commit

This commit is contained in:
Florian Obser 2022-12-02 15:51:07 +01:00
commit 2034e94d7e
16 changed files with 4103 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.html

198
algorithm-roll.org Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

368
fuzzing-ping.org Normal file
View File

@ -0,0 +1,368 @@
#+TITLE: Fuzzing ping(8)
#+SUBTITLE: ... and finding a 24 year old bug.
#+DATE: 2022-12-01
* Prologue
[[https://freebsd.org][FreeBSD]] had a [[https://www.freebsd.org/security/advisories/FreeBSD-SA-22:15.ping.asc][security fluctuation]] in their implementation of =ping(8)=
the other day. As someone who has done a lot of work on [[https://man.openbsd.org/man/ping.8][=ping(8)=]] in
[[https://openbsd.org][OpenBSD]] this tickled my interests.
* What about OpenBSD?
=ping(8)= is ancient:
#+begin_example
* Author -
* Mike Muuss
* U. S. Army Ballistic Research Laboratory
* December, 1983
#+end_example
What we know today as =ping(8)= started to become recognizable in 1986, for
example see this [[https://github.com/csrg/csrg/commit/962056110ebf62ed8d4368964c7e82ac7434ea82][csrg commit]].
FreeBSD identified a stack overflow in the =pr_pack()= function and I
expected a lot of similarity between the BSDs. This stuff did not
change a lot since the csrg days.
Step one: Does this effect us? Turns out, it does not. FreeBSD rewrote
=pr_pack()= in [[https://github.com/freebsd/freebsd-src/commit/d9cacf605e2ac0f704e1ce76357cbfbe6cb63d52][2019]], citing alignment problems.
Now we could join the punters on the Internet and point and laugh. But
that's just rude, uncalled for, and generally boring and
pointless. Technically I'm on vacation and I had resolved to only do
fun things this week. So let's have some fun.
Step two: FreeBSD had a problem in =pr_pack()= because that function
handles data from the network. The data is untrusted and needs to be
validated. Now is a good time as any to check OpenBSD's implementation
of =pr_pack()=. I wanted to try fuzzing something, anything, with [[https://en.wikipedia.org/wiki/American_fuzzy_lop_(fuzzer)][afl]]
for a few years, but never got around to it. I thought I might as well
do it now, might be fun.
* Make sure you are not holding it wrong.
I installed =afl++= from packages and glanced at "[[https://aflplus.plus/docs/tutorials/libxml2_tutorial/][Fuzzing libxml2 with
AFL++]]".
Here is what we need:
+ A program to test. Something with a know bug so that we can tell the
fuzzing works.
+ A file as input, that does not trigger the bug.
+ Compile the program with =afl-clang-fast=.
+ Run =afl-fuzz=.
=test.c:=
#+begin_src C
/* Written by Florian Obser, Public Domain */
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(int argc, char **argv)
{
FILE *f;
size_t fsize;
uint8_t *buf, len, *dbuf;
f = fopen(argv[1], "rb");
fseek(f, 0, SEEK_END);
fsize = ftell(f);
rewind(f);
buf = malloc(fsize + 1);
if (buf == NULL)
err(1, NULL);
fread(buf, fsize, 1, f);
fclose(f);
buf[fsize] = 0;
len = buf[0];
dbuf = malloc(len);
if (dbuf == NULL)
err(1, NULL);
memcpy(buf + 1, dbuf, fsize - 1);
warnx("len: %d", len);
return 0;
}
#+end_src
This program has a trivial buffer overflow. It figures out how big a
file is on disk and stores this in =fsize=. Then it goes ahead and
reads the whole file into a buffer. It interprets the first byte as
the length of data (=len=) and allocates a new buffer (=dbuf=) of this
size. It skips the length byte and copies =fsize - 1= bytes into the
new buffer. So it trusts that the amount of data it read from disk is
the same as indicated by the length byte.
While this might seem silly, this is how real world buffer overflows
look like.
Here is a file where the length byte and file size agree. Create
folders =in= and =out= and place =test.txt= into =in/test.txt=. Don't
forget the newline.
=test.txt=:
#+begin_example
ABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
#+end_example
Compile =test.c=:
#+begin_src shell
CC=/usr/local/bin/afl-clang-fast make test
#+end_src
and run =afl-fuzz=:
#+begin_src shell
afl-fuzz -i in/ -o out -- ./test @@
#+end_src
It more or less immediately finds a crash. The reproducer(s) are in
=out/default/crashes/=.
* Fuzzing =ping(8)=
At this point we are facing a few problems. What does it mean to fuzz
=ping(8)=, where are we getting the sample input from and how do we feed
it to =ping(8)=.
From a high level point of view =ping(8)= parses arguments, initializes
a bunch of stuff and then enters an infinite loop sending ICMP echo
packets and waiting for a reply. It then parses and prints the reply.
Parsing the reply is the interesting thing. The reply comes from the
network and is untrusted. This is where things go wrong. The parsing
is handled by =pr_pack()=, so that's what we should fuzz.
** =in/= for =ping(8)=
Now we need sample data. An ICMP package is binary data
on-wire. Crafting it by hand is annoying. So let's just hack =ping(8)=
to dump the packet to disk.
#+begin_src diff
diff --git sbin/ping/ping.c sbin/ping/ping.c
index a3b3d650eb5..78b571b95b4 100644
--- sbin/ping/ping.c
+++ sbin/ping/ping.c
@@ -79,6 +79,7 @@
#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/time.h>
#include <sys/uio.h>
@@ -95,6 +96,7 @@
#include <ctype.h>
#include <err.h>
#include <errno.h>
+#include <fcntl.h>
#include <limits.h>
#include <math.h>
#include <poll.h>
@@ -217,6 +219,8 @@ const char *pr_addr(struct sockaddr *, socklen_t);
void pr_pack(u_char *, int, struct msghdr *);
__dead void usage(void);
+void output(char *, u_char *, int);
+
/* IPv4 specific functions */
void pr_ipopt(int, u_char *);
int in_cksum(u_short *, int);
@@ -255,7 +259,7 @@ main(int argc, char *argv[])
int df = 0, tos = 0, bufspace = IP_MAXPACKET, hoplimit = -1, mflag = 0;
u_char *datap, *packet;
u_char ttl = MAXTTL;
- char *e, *target, hbuf[NI_MAXHOST], *source = NULL;
+ char *e, *target, hbuf[NI_MAXHOST], *source = NULL, *output_path = NULL;
char rspace[3 + 4 * NROUTES + 1]; /* record route space */
const char *errstr;
double fraction, integral, seconds;
@@ -264,11 +268,13 @@ main(int argc, char *argv[])
u_int rtableid = 0;
extern char *__progname;
+#if 0
/* Cannot pledge due to special setsockopt()s below */
if (unveil("/", "r") == -1)
err(1, "unveil /");
if (unveil(NULL, NULL) == -1)
err(1, "unveil");
+#endif
if (strcmp("ping6", __progname) == 0) {
v6flag = 1;
@@ -297,8 +303,8 @@ main(int argc, char *argv[])
preload = 0;
datap = &outpack[ECHOLEN + ECHOTMLEN];
while ((ch = getopt(argc, argv, v6flag ?
- "c:DdEefgHh:I:i:Ll:mNnp:qS:s:T:V:vw:" :
- "DEI:LRS:c:defgHi:l:np:qs:T:t:V:vw:")) != -1) {
+ "c:DdEefgHh:I:i:Ll:mNno:p:qS:s:T:V:vw:" :
+ "DEI:LRS:c:defgHi:l:no:p:qs:T:t:V:vw:")) != -1) {
switch(ch) {
case 'c':
npackets = strtonum(optarg, 0, INT64_MAX, &errstr);
@@ -375,6 +381,9 @@ main(int argc, char *argv[])
case 'n':
options &= ~F_HOSTNAME;
break;
+ case 'o':
+ output_path = optarg;
+ break;
case 'p': /* fill buffer with user pattern */
options |= F_PINGFILLED;
fill((char *)datap, optarg);
@@ -768,10 +777,10 @@ main(int argc, char *argv[])
}
if (options & F_HOSTNAME) {
- if (pledge("stdio inet dns", NULL) == -1)
+ if (pledge("stdio inet dns wpath cpath", NULL) == -1)
err(1, "pledge");
} else {
- if (pledge("stdio inet", NULL) == -1)
+ if (pledge("stdio inet wpath cpath", NULL) == -1)
err(1, "pledge");
}
@@ -960,8 +969,11 @@ main(int argc, char *argv[])
}
}
continue;
- } else
+ } else {
+ if (output_path != NULL)
+ output(output_path, packet, cc);
pr_pack(packet, cc, &m);
+ }
if (npackets && nreceived >= npackets)
break;
@@ -2274,3 +2286,29 @@ usage(void)
}
exit(1);
}
+
+void
+output(char *path, u_char *pack, int len)
+{
+ size_t bsz, off;
+ ssize_t nw;
+ int fd;
+ char *fname;
+
+ bsz = len;
+ if (asprintf(&fname, "%s/ping_%lld_%d.out", path, time(NULL),
+ getpid()) == -1)
+ err(1, NULL);
+
+ fd = open(fname, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP |
+ S_IROTH);
+ free(fname);
+
+ if (fd == -1)
+ err(1, "open");
+
+ for (off = 0; off < bsz; off += nw)
+ if ((nw = write(fd, pack + off, bsz - off)) == 0 || nw == -1)
+ err(1, "write");
+ close(fd);
+}
#+end_src
After building and installing our hacked version of =ping(8)= we can
create sample input data for afl thusly:
#+begin_src shell
while :; do
ping -o ./in/ -w 1 -c 1 \
$(jot -r 0 255 | head -4 | tr '\n' '.' | sed 's/.$//')
done
#+end_src
=jot= creates a stream of random numbers between 0 and 255, we get the
first four, concatenate them with '.' and cut of the trailing
dot. Voilà we have a bunch of random IPv4 addresses. We then send a
single ping and wait for one second. The ICMP reply is written to
=./in/=.
** Fuzzing =pr_pack()=
At this point I wrote a =main()= function that accepts a file name as
argument, and reads it into a buffer. I then ripped =pr_pack()= out of
=ping(8)= and fed it the file contents.
Of course compiling fails quite spectacularly at this point. So I
added a bunch of missing functions, defines and global variables. It
gets pretty close now. We don't have the =msghdr= from =recvfrom(2)= so
we need to =#if 0= some code. We also need to get rid of the
validation of the data packet using =SipHash= because the whole point
is that the data does not validate and =SipHash= would short circuit.
Oh yeah, and the thing is legacy IP only at this point.
So [[file:fuzzing-ping/afl_ping.c][here (=afl_ping.c=)]] it is, it is quite terrible. It would probably make more sense
to copy all of =ping(8)= and slap on a new =main()= function. Maybe.
Anyway, at this point I was 30 minutes in, from reading about afl for
the first time until firing up =afl-fuzz= on my hacked
=pr_pack()=. Not too bad. It was now time for dinner and I left the
thing running.
** The promised bug
I came back after dinner and afl found zero crashes. That's
disappointing. Or good. Depending on how you look at it. But it found
hangs. Running =afl_ping= on one of the reproducers, it printed
=unknown option 20= forever.
The problem is in this part of the code:
#+begin_src C
for (; hlen > (int)sizeof(struct ip); --hlen, ++cp) {
/* [...] */
switch (*cp) {
/* [...] */
default:
printf("\nunknown option %x", *cp);
hlen = hlen - (cp[IPOPT_OLEN] - 1);
cp = cp + (cp[IPOPT_OLEN] - 1);
break;
}
}
#+end_src
=cp= is untrusted data and if =cp[IPOPT_OLEN]= is zero we would
increase =hlen= by one and the for loop would subtract one, same for
=cp=. We never make any progress and spin forever.
The diff is fairly simple:
#+begin_src diff
diff --git ping.c ping.c
index fb31365ad31..6019c87d8db 100644
--- ping.c
+++ ping.c
@@ -1525,8 +1525,11 @@ pr_ipopt(int hlen, u_char *buf)
break;
default:
printf("\nunknown option %x", *cp);
- hlen = hlen - (cp[IPOPT_OLEN] - 1);
- cp = cp + (cp[IPOPT_OLEN] - 1);
+ if (cp[IPOPT_OLEN] > 0 && (cp[IPOPT_OLEN] - 1) <= hlen) {
+ hlen = hlen - (cp[IPOPT_OLEN] - 1);
+ cp = cp + (cp[IPOPT_OLEN] - 1);
+ } else
+ hlen = 0;
break;
}
}
#+end_src
I foolishly tweaked the diff after collecting OKs and of course the
tweak was wrong. Note to self: Never do this. So it's spread out over
two commits: [[https://cvsweb.openbsd.org/src/sbin/ping/ping.c#rev1.247][ping.c, Revision 1.247]] and [[https://cvsweb.openbsd.org/src/sbin/ping/ping.c#rev1.248][ping.c, Revision 1.248]].
This bug was introduced April 3rd, 1998 in [[https://cvsweb.openbsd.org/src/sbin/ping/ping.c#rev1.30][revision 1.30]], over 24
years ago.
* Epilogue
Afl uses files to feed data to programs to get them to crash or
otherwise misbehave. I had wondered for a few years how I could use
afl with things that talk to the network. Because that's what I mostly
work on. In hindsight it's quite obvious. You identify the main
parsing function, wrap it in a new =main()= function and Robert is
your father's nearest male relative.
The two main takeaways from this are: One, if someone messes up
somewhere, go look if you messed up in the same or similar way
somewhere else. Two, afl is pretty easy to use, even for network
programs. 30 minutes from reading about afl for the first time to
finding a bug in a real world program is pretty neat.

892
fuzzing-ping/afl_ping.c Normal file
View File

@ -0,0 +1,892 @@
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* 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. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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.
*/
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Mike Muuss.
*
* 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. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*/
/*
* Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
* measure round-trip-delays and packet loss across network paths.
*
* Author -
* Mike Muuss
* U. S. Army Ballistic Research Laboratory
* December, 1983
*
* Status -
* Public Domain. Distribution Unlimited.
* Bugs -
* More statistics could always be gathered.
* This program has to run SUID to ROOT to access the ICMP socket.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip_var.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/ip_ah.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <math.h>
#include <poll.h>
#include <pwd.h>
#include <signal.h>
#include <siphash.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define F_FLOOD 0x0001
#define F_INTERVAL 0x0002
#define F_HOSTNAME 0x0004
#define F_PINGFILLED 0x0008
#define F_QUIET 0x0010
#define F_RROUTE 0x0020
#define F_SO_DEBUG 0x0040
#define F_SHOWCHAR 0x0080
#define F_VERBOSE 0x0100
/* 0x0200 */
#define F_HDRINCL 0x0400
#define F_TTL 0x0800
#define F_TOS 0x1000
#define F_AUD_RECV 0x2000
#define F_AUD_MISS 0x4000
#define ECHOLEN 8 /* icmp echo header len excluding time */
#define ECHOTMLEN sizeof(struct payload)
#define DEFDATALEN (64 - ECHOLEN) /* default data length */
#define MAXIPLEN 60
#define MAXICMPLEN 76
#define MAXPAYLOAD (IP_MAXPACKET - MAXIPLEN - ECHOLEN)
#define IP6LEN 40
#define EXTRA 256 /* for AH and various other headers. weird. */
#define MAXPAYLOAD6 IPV6_MAXPACKET - IP6LEN - ECHOLEN
#define MAXWAIT_DEFAULT 10 /* secs to wait for response */
#define NROUTES 9 /* number of record route slots */
struct tv64 {
u_int64_t tv64_sec;
u_int64_t tv64_nsec;
};
struct payload {
struct tv64 tv64;
u_int8_t mac[SIPHASH_DIGEST_LENGTH];
};
const char *pr_addr(struct sockaddr *, socklen_t);
void pr_pack(u_char *, int, struct msghdr *);
/* IPv4 specific functions */
void pr_ipopt(int, u_char *);
int in_cksum(u_short *, int);
void pr_icmph(struct icmp *);
void pr_retip(struct ip *);
void pr_iph(struct ip *);
int map_tos(char *, int *);
int v6flag = 0;
int options = F_VERBOSE;
char BSPACE = '\b'; /* characters written for flood */
char DOT = '.';
int datalen = DEFDATALEN;
int64_t npackets; /* max packets to transmit */
int64_t nreceived; /* # of packets we got back */
int64_t nrepeats; /* number of duplicates */
int64_t ntransmitted; /* sequence # for outbound packets = #sent */
int64_t nmissedmax = 1; /* max value of ntransmitted - nreceived - 1 */
struct timeval interval = {1, 0}; /* interval between packets */
/* timing */
int timing; /* flag to do timing */
int timinginfo;
unsigned int maxwait = MAXWAIT_DEFAULT; /* max seconds to wait for response */
double tmin = 999999999.0; /* minimum round trip time */
double tmax; /* maximum round trip time */
double tsum; /* sum of all times, for doing average */
double tsumsq; /* sum of all times squared, for std. dev. */
struct tv64 tv64_offset;
/*
* MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
* number of received sequence numbers we can keep track of. Change 128
* to 8192 for complete accuracy...
*/
#define MAX_DUP_CHK (8 * 8192)
int mx_dup_ck = MAX_DUP_CHK;
char rcvd_tbl[MAX_DUP_CHK / 8];
#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
#define SET(bit) (A(bit) |= B(bit))
#define CLR(bit) (A(bit) &= (~B(bit)))
#define TST(bit) (A(bit) & B(bit))
int
main(int argc, char **argv)
{
FILE *f;
size_t fsize;
uint8_t *buf;
f = fopen(argv[1], "rb");
fseek(f, 0, SEEK_END);
fsize = ftell(f);
rewind(f);
buf = malloc(fsize + 1);
if (buf == NULL)
err(1, NULL);
fread(buf, fsize, 1, f);
fclose(f);
pr_pack(buf, fsize, NULL);
}
void
pr_pack(u_char *buf, int cc, struct msghdr *mhdr)
{
struct ip *ip = NULL;
struct icmp *icp = NULL;
struct icmp6_hdr *icp6 = NULL;
struct timespec ts, tp;
struct payload payload;
struct sockaddr *from;
socklen_t fromlen;
double triptime = 0;
int i, dupflag;
int hlen = -1, hoplim = -1, echo_reply = 0;
u_int16_t seq;
u_char *cp, *dp;
char* pkttime;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
err(1, "clock_gettime(CLOCK_MONOTONIC)");
if (v6flag) {
#if 0
if (!mhdr || !mhdr->msg_name ||
mhdr->msg_namelen != sizeof(struct sockaddr_in6) ||
((struct sockaddr *)mhdr->msg_name)->sa_family !=
AF_INET6) {
if (options & F_VERBOSE)
warnx("invalid peername");
return;
}
from = (struct sockaddr *)mhdr->msg_name;
fromlen = mhdr->msg_namelen;
if (cc < sizeof(struct icmp6_hdr)) {
if (options & F_VERBOSE)
warnx("packet too short (%d bytes) from %s", cc,
pr_addr(from, fromlen));
return;
}
icp6 = (struct icmp6_hdr *)buf;
if ((hoplim = get_hoplim(mhdr)) == -1) {
warnx("failed to get receiving hop limit");
return;
}
if (icp6->icmp6_type == ICMP6_ECHO_REPLY) {
if (icp6->icmp6_id != ident)
return; /* 'Twas not our ECHO */
seq = icp6->icmp6_seq;
echo_reply = 1;
pkttime = (char *)(icp6 + 1);
}
#endif
} else {
#if 0
if (!mhdr || !mhdr->msg_name ||
mhdr->msg_namelen != sizeof(struct sockaddr_in) ||
((struct sockaddr *)mhdr->msg_name)->sa_family != AF_INET) {
if (options & F_VERBOSE)
warnx("invalid peername");
return;
}
from = (struct sockaddr *)mhdr->msg_name;
fromlen = mhdr->msg_namelen;
#endif
/* Check the IP header */
ip = (struct ip *)buf;
hlen = ip->ip_hl << 2;
if (cc < hlen + ICMP_MINLEN) {
if (options & F_VERBOSE)
warnx("packet too short (%d bytes) from %s", cc,
pr_addr(from, fromlen));
return;
}
/* Now the ICMP part */
cc -= hlen;
icp = (struct icmp *)(buf + hlen);
if (icp->icmp_type == ICMP_ECHOREPLY) {
#if 0
if (icp->icmp_id != ident)
return; /* 'Twas not our ECHO */
#endif
seq = icp->icmp_seq;
echo_reply = 1;
pkttime = (char *)icp->icmp_data;
}
}
if (echo_reply) {
++nreceived;
if (cc >= ECHOLEN + ECHOTMLEN) {
SIPHASH_CTX ctx;
struct tv64 *tv64;
u_int8_t mac[SIPHASH_DIGEST_LENGTH];
memcpy(&payload, pkttime, sizeof(payload));
tv64 = &payload.tv64;
#if 0
SipHash24_Init(&ctx, &mac_key);
SipHash24_Update(&ctx, tv64, sizeof(*tv64));
SipHash24_Update(&ctx, &ident, sizeof(ident));
SipHash24_Update(&ctx, &seq, sizeof(seq));
SipHash24_Final(mac, &ctx);
if (timingsafe_memcmp(mac, &payload.mac,
sizeof(mac)) != 0) {
printf("signature mismatch from %s: "
"icmp_seq=%u\n", pr_addr(from, fromlen),
ntohs(seq));
--nreceived;
return;
}
#endif
timinginfo=1;
tp.tv_sec = betoh64(tv64->tv64_sec) -
tv64_offset.tv64_sec;
tp.tv_nsec = betoh64(tv64->tv64_nsec) -
tv64_offset.tv64_nsec;
timespecsub(&ts, &tp, &ts);
triptime = ((double)ts.tv_sec) * 1000.0 +
((double)ts.tv_nsec) / 1000000.0;
tsum += triptime;
tsumsq += triptime * triptime;
if (triptime < tmin)
tmin = triptime;
if (triptime > tmax)
tmax = triptime;
}
if (TST(ntohs(seq) % mx_dup_ck)) {
++nrepeats;
--nreceived;
dupflag = 1;
} else {
SET(ntohs(seq) % mx_dup_ck);
dupflag = 0;
}
if (options & F_QUIET)
return;
if (options & F_FLOOD)
write(STDOUT_FILENO, &BSPACE, 1);
else if (options & F_SHOWCHAR) {
if (dupflag)
putchar('D');
else if (cc - ECHOLEN < datalen)
putchar('T');
else
putchar('!');
} else {
printf("%d bytes from %s: icmp_seq=%u", cc,
pr_addr(from, fromlen), ntohs(seq));
if (v6flag)
printf(" hlim=%d", hoplim);
else
printf(" ttl=%d", ip->ip_ttl);
if (cc >= ECHOLEN + ECHOTMLEN)
printf(" time=%.3f ms", triptime);
if (dupflag)
printf(" (DUP!)");
/* check the data */
if (cc - ECHOLEN < datalen)
printf(" (TRUNC!)");
if (v6flag)
cp = buf + ECHOLEN + ECHOTMLEN;
else
cp = (u_char *)&icp->icmp_data[ECHOTMLEN];
#if 0
dp = &outpack[ECHOLEN + ECHOTMLEN];
for (i = ECHOLEN + ECHOTMLEN;
i < cc && i < datalen;
++i, ++cp, ++dp) {
if (*cp != *dp) {
printf("\nwrong data byte #%d "
"should be 0x%x but was 0x%x",
i - ECHOLEN, *dp, *cp);
if (v6flag)
cp = buf + ECHOLEN;
else
cp = (u_char *)
&icp->icmp_data[0];
for (i = ECHOLEN; i < cc && i < datalen;
++i, ++cp) {
if ((i % 32) == 8)
printf("\n\t");
printf("%x ", *cp);
}
break;
}
}
#endif
}
} else {
/* We've got something other than an ECHOREPLY */
if (!(options & F_VERBOSE))
return;
printf("%d bytes from %s: ", cc, pr_addr(from, fromlen));
#if 0
if (v6flag)
pr_icmph6(icp6, buf + cc);
else
#endif
pr_icmph(icp);
}
/* Display any IP options */
if (!v6flag && hlen > sizeof(struct ip))
pr_ipopt(hlen, buf);
if (!(options & F_FLOOD)) {
if (!(options & F_SHOWCHAR)) {
putchar('\n');
#if 0
if (v6flag && (options & F_VERBOSE))
pr_exthdrs(mhdr);
#endif
}
fflush(stdout);
if (options & F_AUD_RECV)
fputc('\a', stderr);
}
}
void
pr_ipopt(int hlen, u_char *buf)
{
static int old_rrlen;
static char old_rr[MAX_IPOPTLEN];
struct sockaddr_in s_in;
in_addr_t l;
u_int i, j;
u_char *cp;
cp = buf + sizeof(struct ip);
s_in.sin_len = sizeof(s_in);
s_in.sin_family = AF_INET;
for (; hlen > (int)sizeof(struct ip); --hlen, ++cp) {
switch (*cp) {
case IPOPT_EOL:
hlen = 0;
break;
case IPOPT_LSRR:
printf("\nLSRR: ");
hlen -= 2;
j = *++cp;
++cp;
i = 0;
if (j > IPOPT_MINOFF) {
for (;;) {
l = *++cp;
l = (l<<8) + *++cp;
l = (l<<8) + *++cp;
l = (l<<8) + *++cp;
if (l == 0)
printf("\t0.0.0.0");
else {
s_in.sin_addr.s_addr = ntohl(l);
printf("\t%s",
pr_addr((struct sockaddr*)
&s_in, sizeof(s_in)));
}
hlen -= 4;
j -= 4;
i += 4;
if (j <= IPOPT_MINOFF)
break;
if (i >= MAX_IPOPTLEN) {
printf("\t(truncated route)");
break;
}
putchar('\n');
}
}
break;
case IPOPT_RR:
j = *++cp; /* get length */
i = *++cp; /* and pointer */
hlen -= 2;
if (i > j)
i = j;
i -= IPOPT_MINOFF;
if (i <= 0)
continue;
if (i == old_rrlen &&
cp == buf + sizeof(struct ip) + 2 &&
!memcmp(cp, old_rr, i) &&
!(options & F_FLOOD)) {
printf("\t(same route)");
i = (i + 3) & ~0x3;
hlen -= i;
cp += i;
break;
}
if (i < MAX_IPOPTLEN) {
old_rrlen = i;
memcpy(old_rr, cp, i);
} else
old_rrlen = 0;
printf("\nRR: ");
j = 0;
for (;;) {
l = *++cp;
l = (l<<8) + *++cp;
l = (l<<8) + *++cp;
l = (l<<8) + *++cp;
if (l == 0)
printf("\t0.0.0.0");
else {
s_in.sin_addr.s_addr = ntohl(l);
printf("\t%s",
pr_addr((struct sockaddr*)&s_in,
sizeof(s_in)));
}
hlen -= 4;
i -= 4;
j += 4;
if (i <= 0)
break;
if (j >= MAX_IPOPTLEN) {
printf("\t(truncated route)");
break;
}
putchar('\n');
}
break;
case IPOPT_NOP:
printf("\nNOP");
break;
default:
printf("\nunknown option %x", *cp);
if (cp[IPOPT_OLEN] > 0 && (cp[IPOPT_OLEN] - 1) <= hlen) {
hlen = hlen - (cp[IPOPT_OLEN] - 1);
cp = cp + (cp[IPOPT_OLEN] - 1);
} else
hlen = 0;
break;
}
}
}
/*
* pr_addr --
* Return address in numeric form or a host name
*/
const char *
pr_addr(struct sockaddr *addr, socklen_t addrlen)
{
static char buf[NI_MAXHOST];
int flag = 0;
return "not implemented";
if (!(options & F_HOSTNAME))
flag |= NI_NUMERICHOST;
if (getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, flag) == 0)
return (buf);
else
return "?";
}
/*
* pr_icmph --
* Print a descriptive string about an ICMP header.
*/
void
pr_icmph(struct icmp *icp)
{
switch(icp->icmp_type) {
case ICMP_ECHOREPLY:
printf("Echo Reply\n");
/* XXX ID + Seq + Data */
break;
case ICMP_UNREACH:
switch(icp->icmp_code) {
case ICMP_UNREACH_NET:
printf("Destination Net Unreachable\n");
break;
case ICMP_UNREACH_HOST:
printf("Destination Host Unreachable\n");
break;
case ICMP_UNREACH_PROTOCOL:
printf("Destination Protocol Unreachable\n");
break;
case ICMP_UNREACH_PORT:
printf("Destination Port Unreachable\n");
break;
case ICMP_UNREACH_NEEDFRAG:
if (icp->icmp_nextmtu != 0)
printf("frag needed and DF set (MTU %d)\n",
ntohs(icp->icmp_nextmtu));
else
printf("frag needed and DF set\n");
break;
case ICMP_UNREACH_SRCFAIL:
printf("Source Route Failed\n");
break;
case ICMP_UNREACH_NET_UNKNOWN:
printf("Network Unknown\n");
break;
case ICMP_UNREACH_HOST_UNKNOWN:
printf("Host Unknown\n");
break;
case ICMP_UNREACH_ISOLATED:
printf("Source Isolated\n");
break;
case ICMP_UNREACH_NET_PROHIB:
printf("Dest. Net Administratively Prohibited\n");
break;
case ICMP_UNREACH_HOST_PROHIB:
printf("Dest. Host Administratively Prohibited\n");
break;
case ICMP_UNREACH_TOSNET:
printf("Destination Net Unreachable for TOS\n");
break;
case ICMP_UNREACH_TOSHOST:
printf("Destination Host Unreachable for TOS\n");
break;
case ICMP_UNREACH_FILTER_PROHIB:
printf("Route administratively prohibited\n");
break;
case ICMP_UNREACH_HOST_PRECEDENCE:
printf("Host Precedence Violation\n");
break;
case ICMP_UNREACH_PRECEDENCE_CUTOFF:
printf("Precedence Cutoff\n");
break;
default:
printf("Dest Unreachable, Unknown Code: %d\n",
icp->icmp_code);
break;
}
/* Print returned IP header information */
pr_retip((struct ip *)icp->icmp_data);
break;
case ICMP_SOURCEQUENCH:
printf("Source Quench\n");
pr_retip((struct ip *)icp->icmp_data);
break;
case ICMP_REDIRECT:
switch(icp->icmp_code) {
case ICMP_REDIRECT_NET:
printf("Redirect Network");
break;
case ICMP_REDIRECT_HOST:
printf("Redirect Host");
break;
case ICMP_REDIRECT_TOSNET:
printf("Redirect Type of Service and Network");
break;
case ICMP_REDIRECT_TOSHOST:
printf("Redirect Type of Service and Host");
break;
default:
printf("Redirect, Unknown Code: %d", icp->icmp_code);
break;
}
printf("(New addr: %s)\n",
inet_ntoa(icp->icmp_gwaddr));
pr_retip((struct ip *)icp->icmp_data);
break;
case ICMP_ECHO:
printf("Echo Request\n");
/* XXX ID + Seq + Data */
break;
case ICMP_ROUTERADVERT:
/* RFC1256 */
printf("Router Discovery Advertisement\n");
printf("(%d entries, lifetime %d seconds)\n",
icp->icmp_num_addrs, ntohs(icp->icmp_lifetime));
break;
case ICMP_ROUTERSOLICIT:
/* RFC1256 */
printf("Router Discovery Solicitation\n");
break;
case ICMP_TIMXCEED:
switch(icp->icmp_code) {
case ICMP_TIMXCEED_INTRANS:
printf("Time to live exceeded\n");
break;
case ICMP_TIMXCEED_REASS:
printf("Frag reassembly time exceeded\n");
break;
default:
printf("Time exceeded, Unknown Code: %d\n",
icp->icmp_code);
break;
}
pr_retip((struct ip *)icp->icmp_data);
break;
case ICMP_PARAMPROB:
switch(icp->icmp_code) {
case ICMP_PARAMPROB_OPTABSENT:
printf("Parameter problem, required option "
"absent: pointer = 0x%02x\n",
ntohs(icp->icmp_hun.ih_pptr));
break;
default:
printf("Parameter problem: pointer = 0x%02x\n",
ntohs(icp->icmp_hun.ih_pptr));
break;
}
pr_retip((struct ip *)icp->icmp_data);
break;
case ICMP_TSTAMP:
printf("Timestamp\n");
/* XXX ID + Seq + 3 timestamps */
break;
case ICMP_TSTAMPREPLY:
printf("Timestamp Reply\n");
/* XXX ID + Seq + 3 timestamps */
break;
case ICMP_IREQ:
printf("Information Request\n");
/* XXX ID + Seq */
break;
case ICMP_IREQREPLY:
printf("Information Reply\n");
/* XXX ID + Seq */
break;
case ICMP_MASKREQ:
printf("Address Mask Request\n");
break;
case ICMP_MASKREPLY:
printf("Address Mask Reply (Mask 0x%08x)\n",
ntohl(icp->icmp_mask));
break;
default:
printf("Unknown ICMP type: %d\n", icp->icmp_type);
}
}
/*
* pr_iph --
* Print an IP header with options.
*/
void
pr_iph(struct ip *ip)
{
int hlen;
u_char *cp;
hlen = ip->ip_hl << 2;
cp = (u_char *)ip + 20; /* point to options */
printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n");
printf(" %1x %1x %02x %04x %04x",
ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id);
printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13,
(ip->ip_off) & 0x1fff);
printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum);
printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr));
printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr));
/* dump and option bytes */
while (hlen-- > 20) {
printf("%02x", *cp++);
}
putchar('\n');
}
/*
* pr_retip --
* Dump some info on a returned (via ICMP) IP packet.
*/
void
pr_retip(struct ip *ip)
{
int hlen;
u_char *cp;
pr_iph(ip);
hlen = ip->ip_hl << 2;
cp = (u_char *)ip + hlen;
if (ip->ip_p == 6)
printf("TCP: from port %u, to port %u (decimal)\n",
(*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
else if (ip->ip_p == 17)
printf("UDP: from port %u, to port %u (decimal)\n",
(*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
}
#ifndef SMALL
int
map_tos(char *key, int *val)
{
/* DiffServ Codepoints and other TOS mappings */
const struct toskeywords {
const char *keyword;
int val;
} *t, toskeywords[] = {
{ "af11", IPTOS_DSCP_AF11 },
{ "af12", IPTOS_DSCP_AF12 },
{ "af13", IPTOS_DSCP_AF13 },
{ "af21", IPTOS_DSCP_AF21 },
{ "af22", IPTOS_DSCP_AF22 },
{ "af23", IPTOS_DSCP_AF23 },
{ "af31", IPTOS_DSCP_AF31 },
{ "af32", IPTOS_DSCP_AF32 },
{ "af33", IPTOS_DSCP_AF33 },
{ "af41", IPTOS_DSCP_AF41 },
{ "af42", IPTOS_DSCP_AF42 },
{ "af43", IPTOS_DSCP_AF43 },
{ "critical", IPTOS_PREC_CRITIC_ECP },
{ "cs0", IPTOS_DSCP_CS0 },
{ "cs1", IPTOS_DSCP_CS1 },
{ "cs2", IPTOS_DSCP_CS2 },
{ "cs3", IPTOS_DSCP_CS3 },
{ "cs4", IPTOS_DSCP_CS4 },
{ "cs5", IPTOS_DSCP_CS5 },
{ "cs6", IPTOS_DSCP_CS6 },
{ "cs7", IPTOS_DSCP_CS7 },
{ "ef", IPTOS_DSCP_EF },
{ "inetcontrol", IPTOS_PREC_INTERNETCONTROL },
{ "lowdelay", IPTOS_LOWDELAY },
{ "netcontrol", IPTOS_PREC_NETCONTROL },
{ "reliability", IPTOS_RELIABILITY },
{ "throughput", IPTOS_THROUGHPUT },
{ NULL, -1 },
};
for (t = toskeywords; t->keyword != NULL; t++) {
if (strcmp(key, t->keyword) == 0) {
*val = t->val;
return (1);
}
}
return (0);
}
#endif /* SMALL */
#if 0
void
pr_exthdrs(struct msghdr *mhdr)
{
struct cmsghdr *cm;
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
if (cm->cmsg_level != IPPROTO_IPV6)
continue;
switch (cm->cmsg_type) {
case IPV6_HOPOPTS:
printf(" HbH Options: ");
pr_ip6opt(CMSG_DATA(cm));
break;
case IPV6_DSTOPTS:
case IPV6_RTHDRDSTOPTS:
printf(" Dst Options: ");
pr_ip6opt(CMSG_DATA(cm));
break;
case IPV6_RTHDR:
printf(" Routing: ");
pr_rthdr(CMSG_DATA(cm));
break;
}
}
}
#endif

2455
htmlize.css Normal file

File diff suppressed because it is too large Load Diff

19
index.org Normal file
View File

@ -0,0 +1,19 @@
* Ramblings
- [[file:fuzzing-ping.org][2022-12-01: Fuzzing ping(8)]]
- [[file:algorithm-roll.org][2022-06-08: DNSSEC algorithm roll-over]]
- [[file:nsd-unbound-update.org][2020-12-07: nsd(8) / unbound(8) update]]
- [[file:secrets-manager.org][2019-10-20: Secrets Manager]]
- [[file:ports-hints.org][2014-12-21: ports hints]]
* External Writings & Presentations
- [[http://undeadly.org/cgi?action=article&sid=20131025090233][2013-10-25, undeadly: b2k13 hackathon report: Florian Obser (florian@) on nginx.conf(5), slowcgi]]
- [[http://undeadly.org/cgi?action=article&sid=20140721125020][2014-07-21, undeadly: g2k14: Florian Obser in IPv6 land]]
- [[http://undeadly.org/cgi?action=article&sid=20150805151453][2015-08-05, undeadly: c2k15: florian@ on building the hackathon network, httpd and pflow]]
- [[http://undeadly.org/cgi?action=article&sid=20151113082512][2015-11-16, undeadly: u2k15: florian@ on IPv6 hackery]]
- [[http://undeadly.org/cgi?action=article&sid=20160911000052][2016-09-10, undeadly: g2k16 Hackathon Report: Florian Obser on httpd, networking, acme-client, and more]]
- [[https://undeadly.org/cgi?action=article;sid=20170609013548][2017-07-09, undeadly: d2k17 Hackathon Report: Florian Obser on slaacd(8)]]
- [[https://undeadly.org/cgi?action=article;sid=20171113235334][2017-11-13, undeadly: p2k17 Hackathon report: Florian Obser on network stack progress, kernel relinking and more]]
- [[http://www.openbsd.org/papers/florian_slaacd_bsdcan2018.pdf][2018-06-09, BSDCan: slaacd(8) - A privilege separated and sandboxed IPv6 Stateless Address AutoConfiguration Daemon]], [[https://www.youtube.com/watch?v=tBQXZYotKvI][video]]
- [[https://undeadly.org/cgi?action=article;sid=20190413023608][2019-04-12, undeadly: t2k19 Hackathon Report: unwinding in Taipei]]
- [[http://www.openbsd.org/papers/bsdcan2019_unwind.pdf][2019-05-17, BSDCan: unwind(8) a privilege-separated, validating DNS recursive nameserver for every laptop]], [[https://www.youtube.com/watch?v=88SoI49nO4o][video]]
- [[https://archive.fosdem.org/2020/schedule/event/dns_unwind/][2020-02-01, FOSDEM: unwind(8) a privilege-separated, validating DNS recursive nameserver for every laptop]]
- [[https://undeadly.org/cgi?action=article;sid=20200922090542][2020-09-21, undeadly: k2k20 hackathon report: Florian Obser on DNS]]

48
nsd-unbound-update.org Normal file
View File

@ -0,0 +1,48 @@
#+TITLE: nsd(8) / unbound(8) update
#+DATE: 2020-12-07
How to update =nsd(8)= and =unbound(8)= in OpenBSD base from NLnetLabs
upstream.
* nsd(8)
git clone from the upstream repository on [[https://github.com/NLnetLabs/nsd][github]].
#+begin_src shell
git pull
git diff NSD_4_3_6_REL..NSD_4_3_7_REL . ':!.cirrus.yml' ':!tpkg/*' \
':!contrib/*' ':!compat/' ':!README.md' ':!.gitignore' \
':!.buildkite/*' ':!makedist.sh' > ~/nsd_4.3.7_upstream.diff
cd /usr/src/usr.sbin/nsd
patch -Ep0 < ~/nsd_4.3.7_upstream.diff
autoheader-2.69
autoconf-2.69
make -f Makefile.bsd-wrapper obj
make -f Makefile.bsd-wrapper clean
make -f Makefile.bsd-wrapper -j4
#+end_src
We also need to update the version numbers in the man pages. For that
we download the release tar ball and generate a diff:
#+begin_src shell
diff -ru . /home/florian/nsd-4.3.7/ | fgrep -v Only > sync.diff
#+end_src
The diff then needs to be partially applied, some changes are
intentional.
* unbound(8)
I haven't done an unbound update in some time, this is probably
outdated.
git clone from the upstream repository on [[https://github.com/NLnetLabs/unbound][github]].
#+begin_src shell
git pull
git diff release-1.15.0..release-1.16.0 . \
':!contrib/*' ':!compat/' ':!README.md' ':!.gitignore' \
':!pythonmod/' ':!testdata/' ':!util/configlexer.c' \
':!util/configparser.c' ':!util/configparser.h' \
':!.buildkite/*' ':!makedist.sh' > ~/unbound_1.16.0_upstream.diff
cd /usr/src/usr.sbin/unbound
patch -Ep0 < ~/unbound_1.16.0_upstream.diff
autoheader-2.69
autoconf-2.69
make -f Makefile.bsd-wrapper obj
make -f Makefile.bsd-wrapper clean
make -f Makefile.bsd-wrapper -j4
#+end_src

22
ports-hints.org Normal file
View File

@ -0,0 +1,22 @@
#+TITLE: ports hints
#+SUBTITLE: Stuff about port building I always forget.
#+DATE: 2014-12-21
See also [[https://man.openbsd.org/bsd.port.mk][bsd.port.mk(5)]].
* Environment variables
+ FETCH_PACKAGES :: Instruct the package target to download packages
missing, only building them if no suitable packages are
found. Defaults to *No*.
For example: src_shell[:exports code]{make FETCH_PACKAGES=-Dsnap package}
* Make targets
+ makesum :: Creates the checksum file.
+ checksum :: Checks the upstream distribution tar ball (or other
archives) against the checksum file.
+ extract :: Extracts the tar ball.
+ build, all :: Build the port.
+ fake :: Do a fake port installation.
+ plist :: Create / update the plist file.
+ package :: Do all the things to create a package.

48
publish.el Executable file
View File

@ -0,0 +1,48 @@
#!/usr/local/bin/emacs --script
(require 'package)
(package-initialize)
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
("elpa" . "https://elpa.gnu.org/packages/")
("org" . "http://orgmode.org/elpa/")))
(unless package-archive-contents
(package-refresh-contents))
(unless (package-installed-p 'use-package)
(package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure t)
(use-package htmlize)
(require 'ox-publish)
(require 'ox-html)
(require 'htmlize)
(require 'org)
(setq org-html-htmlize-output-type 'css)
(setq org-html-validation-link nil)
(setq org-publish-project-alist
'(("tlakh"
:base-directory "/var/www/htdocs/"
:publishing-function org-html-publish-to-html
:publishing-directory "/var/www/htdocs/"
:section-numbers nil
:with-author nil
:with-email nil
:with-timestamps nil
:with-toc nil
:with-creator: nil
:html-head-include-default-style: nil
:html-head "<link rel=\"stylesheet\"
href=\"simple.min.css\"
type=\"text/css\"/>
<link rel=\"stylesheet\"
href=\"htmlize.css\"
type=\"text/css\"/>")))
(org-publish "tlakh")

51
secrets-manager.org Normal file
View File

@ -0,0 +1,51 @@
#+TITLE: Secrets Manager
#+DATE: 2019-10-20
* Intro
It seems like everyone eventually writes their own password
manager. This is my take on it.
+ Simple.
+ Reasonable chance of it still working in 20 years.
+ No dependency on fancy language du jour.
Secrets are stored in files. One file per secret. The file name is
used to identify the secret. Secrets are encrypted / decrypted using
[[https://humungus.tedunangst.com/r/reop][=reop(1)=]]. =reop(1)= is available in packages on OpenBSD and in homebrew
on macOS.
* Scripts
** encrypt
#+begin_src shell
#!/bin/sh
j="$@"
reop -E -m - -x "$j"
#+end_src
** decrypt
#+begin_src shell
#!/bin/sh
echo $1
reop -D -m - -x "$1"
#+end_src
** 2fa
As a bonus here is a script for two factor authentication.
[[https://www.nongnu.org/oath-toolkit/man-oathtool.html][=oathtool(1)=]] is available in packages on OpenBSD and homebrew as
=oath-toolkit=. Whether this is safe depends on your threat model.
#+begin_src shell
#!/bin/sh
/usr/local/bin/oathtool --totp --base32 `reop -D -m - -x "$1"`
#+end_src
* Epilogue
Storing secrets in files, one file per secret, lets us organise our
secrets in a file system hierarchy. This is a thing most people are
already familiar with. The file system hierarchy can be backuped and
tracked in a version control system.
When tracking the secrets in a version control system it is beneficial
to store one secret per file. One can track when the secret got
created and changed. This enables us to revert back to an old version.
When secrets are stored in one big encrypted container changes are
opaque to the version control system. Some change happened, but you
can't find out what changed. This might be a good thing.
A crypto container only tells you: here are some secrets. A secret per
file leaks meta information. If you get access to my secret storage
you can tell that I'm [[https://bsd.network/@florian][@florian@bsd.network]]. But you will not be able
to log in.

1
simple.min.css vendored Normal file

File diff suppressed because one or more lines are too long