xref: /plan9/sys/src/cmd/ssh2/pubkey.c (revision 63afb9a5d3f910047231762bcce0ee49fed3d07c)
1*63afb9a5SDavid du Colombier #include <u.h>
2*63afb9a5SDavid du Colombier #include <libc.h>
3*63afb9a5SDavid du Colombier #include <mp.h>
4*63afb9a5SDavid du Colombier #include <ctype.h>
5*63afb9a5SDavid du Colombier #include <libsec.h>
6*63afb9a5SDavid du Colombier #include <fcall.h>
7*63afb9a5SDavid du Colombier #include <thread.h>
8*63afb9a5SDavid du Colombier #include <9p.h>
9*63afb9a5SDavid du Colombier #include "netssh.h"
10*63afb9a5SDavid du Colombier 
11*63afb9a5SDavid du Colombier enum {
12*63afb9a5SDavid du Colombier 	Arbsz =	256,
13*63afb9a5SDavid du Colombier };
14*63afb9a5SDavid du Colombier 
15*63afb9a5SDavid du Colombier static int
parsepubkey(char * s,RSApub * key,char ** sp,int base)16*63afb9a5SDavid du Colombier parsepubkey(char *s, RSApub *key, char **sp, int base)
17*63afb9a5SDavid du Colombier {
18*63afb9a5SDavid du Colombier 	int n;
19*63afb9a5SDavid du Colombier 	char *host, *p, *z;
20*63afb9a5SDavid du Colombier 
21*63afb9a5SDavid du Colombier 	z = nil;
22*63afb9a5SDavid du Colombier 	n = strtoul(s, &p, 10);
23*63afb9a5SDavid du Colombier 	host = nil;
24*63afb9a5SDavid du Colombier 	if(n < Arbsz || !isspace(*p)){		/* maybe this is a host name */
25*63afb9a5SDavid du Colombier 		host = s;
26*63afb9a5SDavid du Colombier 		s = strpbrk(s, " \t");
27*63afb9a5SDavid du Colombier 		if(s == nil)
28*63afb9a5SDavid du Colombier 			return -1;
29*63afb9a5SDavid du Colombier 		z = s;
30*63afb9a5SDavid du Colombier 		*s++ = '\0';
31*63afb9a5SDavid du Colombier 		s += strspn(s, " \t");
32*63afb9a5SDavid du Colombier 
33*63afb9a5SDavid du Colombier 		n = strtoul(s, &p, 10);
34*63afb9a5SDavid du Colombier 		if(n < Arbsz || !isspace(*p)){
35*63afb9a5SDavid du Colombier 			if(z)
36*63afb9a5SDavid du Colombier 				*z = ' ';
37*63afb9a5SDavid du Colombier 			return -1;
38*63afb9a5SDavid du Colombier 		}
39*63afb9a5SDavid du Colombier 	}
40*63afb9a5SDavid du Colombier 
41*63afb9a5SDavid du Colombier 	/* Arbsz is just a sanity check */
42*63afb9a5SDavid du Colombier 	if((key->ek = strtomp(p, &p, base, nil)) == nil ||
43*63afb9a5SDavid du Colombier 	    (key->n = strtomp(p, &p, base, nil)) == nil ||
44*63afb9a5SDavid du Colombier 	    (*p != '\0' && !isspace(*p)) || mpsignif(key->n) < Arbsz) {
45*63afb9a5SDavid du Colombier 		mpfree(key->ek);
46*63afb9a5SDavid du Colombier 		mpfree(key->n);
47*63afb9a5SDavid du Colombier 		key->ek = nil;
48*63afb9a5SDavid du Colombier 		key->n = nil;
49*63afb9a5SDavid du Colombier 		if(z)
50*63afb9a5SDavid du Colombier 			*z = ' ';
51*63afb9a5SDavid du Colombier 		return -1;
52*63afb9a5SDavid du Colombier 	}
53*63afb9a5SDavid du Colombier 	if(host == nil){
54*63afb9a5SDavid du Colombier 		if(*p != '\0'){
55*63afb9a5SDavid du Colombier 			p += strspn(p, " \t");
56*63afb9a5SDavid du Colombier 			if(*p != '\0'){
57*63afb9a5SDavid du Colombier 				host = emalloc9p(strlen(p)+1);
58*63afb9a5SDavid du Colombier 				strcpy(host, p);
59*63afb9a5SDavid du Colombier 			}
60*63afb9a5SDavid du Colombier 		}
61*63afb9a5SDavid du Colombier 		free(s);
62*63afb9a5SDavid du Colombier 	}
63*63afb9a5SDavid du Colombier 	*sp = host;
64*63afb9a5SDavid du Colombier 	return 0;
65*63afb9a5SDavid du Colombier }
66*63afb9a5SDavid du Colombier 
67*63afb9a5SDavid du Colombier RSApub*
readpublickey(Biobuf * b,char ** sp)68*63afb9a5SDavid du Colombier readpublickey(Biobuf *b, char **sp)
69*63afb9a5SDavid du Colombier {
70*63afb9a5SDavid du Colombier 	char *s;
71*63afb9a5SDavid du Colombier 	RSApub *key;
72*63afb9a5SDavid du Colombier 
73*63afb9a5SDavid du Colombier 	key = emalloc9p(sizeof(RSApub));
74*63afb9a5SDavid du Colombier 	if(key == nil)
75*63afb9a5SDavid du Colombier 		return nil;
76*63afb9a5SDavid du Colombier 
77*63afb9a5SDavid du Colombier 	for (; (s = Brdstr(b, '\n', 1)) != nil; free(s))
78*63afb9a5SDavid du Colombier 		if(s[0] != '#'){
79*63afb9a5SDavid du Colombier 			if(parsepubkey(s, key, sp, 10) == 0 ||
80*63afb9a5SDavid du Colombier 			    parsepubkey(s, key, sp, 16) == 0)
81*63afb9a5SDavid du Colombier 				return key;
82*63afb9a5SDavid du Colombier 			fprint(2, "warning: skipping line '%s'; cannot parse\n",
83*63afb9a5SDavid du Colombier 				s);
84*63afb9a5SDavid du Colombier 		}
85*63afb9a5SDavid du Colombier 	free(key);
86*63afb9a5SDavid du Colombier 	return nil;
87*63afb9a5SDavid du Colombier }
88*63afb9a5SDavid du Colombier 
89*63afb9a5SDavid du Colombier static int
match(char * pattern,char * aliases)90*63afb9a5SDavid du Colombier match(char *pattern, char *aliases)
91*63afb9a5SDavid du Colombier {
92*63afb9a5SDavid du Colombier 	char *s, *snext, *a, *anext, *ae;
93*63afb9a5SDavid du Colombier 
94*63afb9a5SDavid du Colombier 	for(s = pattern; s && *s; s = snext){
95*63afb9a5SDavid du Colombier 		if((snext = strchr(s, ',')) != nil)
96*63afb9a5SDavid du Colombier 			*snext++ = '\0';
97*63afb9a5SDavid du Colombier 		for(a = aliases; a && *a; a = anext){
98*63afb9a5SDavid du Colombier 			if((anext = strchr(a, ',')) != nil){
99*63afb9a5SDavid du Colombier 				ae = anext;
100*63afb9a5SDavid du Colombier 				anext++;
101*63afb9a5SDavid du Colombier 			}else
102*63afb9a5SDavid du Colombier 				ae = a + strlen(a);
103*63afb9a5SDavid du Colombier 			if(ae - a == strlen(s) && memcmp(s, a, ae - a) == 0)
104*63afb9a5SDavid du Colombier 				return 0;
105*63afb9a5SDavid du Colombier 		}
106*63afb9a5SDavid du Colombier 	}
107*63afb9a5SDavid du Colombier 	return 1;
108*63afb9a5SDavid du Colombier }
109*63afb9a5SDavid du Colombier 
110*63afb9a5SDavid du Colombier int
findkey(char * keyfile,char * host,RSApub * key)111*63afb9a5SDavid du Colombier findkey(char *keyfile, char *host, RSApub *key)
112*63afb9a5SDavid du Colombier {
113*63afb9a5SDavid du Colombier 	char *h;
114*63afb9a5SDavid du Colombier 	Biobuf *b;
115*63afb9a5SDavid du Colombier 	RSApub *k;
116*63afb9a5SDavid du Colombier 	int res;
117*63afb9a5SDavid du Colombier 
118*63afb9a5SDavid du Colombier 	if ((b = Bopen(keyfile, OREAD)) == nil)
119*63afb9a5SDavid du Colombier 		return NoKeyFile;
120*63afb9a5SDavid du Colombier 
121*63afb9a5SDavid du Colombier 	for (res = NoKey; res != KeyOk;) {
122*63afb9a5SDavid du Colombier 		if ((k = readpublickey(b, &h)) == nil)
123*63afb9a5SDavid du Colombier 			break;
124*63afb9a5SDavid du Colombier 		if (match(h, host) == 0) {
125*63afb9a5SDavid du Colombier 			if (mpcmp(k->n, key->n) == 0 &&
126*63afb9a5SDavid du Colombier 			    mpcmp(k->ek, key->ek) == 0)
127*63afb9a5SDavid du Colombier 				res = KeyOk;
128*63afb9a5SDavid du Colombier 			else
129*63afb9a5SDavid du Colombier 				res = KeyWrong;
130*63afb9a5SDavid du Colombier 		}
131*63afb9a5SDavid du Colombier 		free(h);
132*63afb9a5SDavid du Colombier 		free(k->ek);
133*63afb9a5SDavid du Colombier 		free(k->n);
134*63afb9a5SDavid du Colombier 		free(k);
135*63afb9a5SDavid du Colombier 	}
136*63afb9a5SDavid du Colombier 	Bterm(b);
137*63afb9a5SDavid du Colombier 	return res;
138*63afb9a5SDavid du Colombier }
139*63afb9a5SDavid du Colombier 
140*63afb9a5SDavid du Colombier int
replacekey(char * keyfile,char * host,RSApub * hostkey)141*63afb9a5SDavid du Colombier replacekey(char *keyfile, char *host, RSApub *hostkey)
142*63afb9a5SDavid du Colombier {
143*63afb9a5SDavid du Colombier 	int ret;
144*63afb9a5SDavid du Colombier 	char *h, *nkey, *p;
145*63afb9a5SDavid du Colombier 	Biobuf *br, *bw;
146*63afb9a5SDavid du Colombier 	Dir *d, nd;
147*63afb9a5SDavid du Colombier 	RSApub *k;
148*63afb9a5SDavid du Colombier 
149*63afb9a5SDavid du Colombier 	ret = -1;
150*63afb9a5SDavid du Colombier 	d = nil;
151*63afb9a5SDavid du Colombier 	nkey = smprint("%s.new", keyfile);
152*63afb9a5SDavid du Colombier 	if(nkey == nil)
153*63afb9a5SDavid du Colombier 		return -1;
154*63afb9a5SDavid du Colombier 
155*63afb9a5SDavid du Colombier 	if((br = Bopen(keyfile, OREAD)) == nil)
156*63afb9a5SDavid du Colombier 		goto out;
157*63afb9a5SDavid du Colombier 	if((bw = Bopen(nkey, OWRITE)) == nil){
158*63afb9a5SDavid du Colombier 		Bterm(br);
159*63afb9a5SDavid du Colombier 		goto out;
160*63afb9a5SDavid du Colombier 	}
161*63afb9a5SDavid du Colombier 
162*63afb9a5SDavid du Colombier 	while((k = readpublickey(br, &h)) != nil){
163*63afb9a5SDavid du Colombier 		if(match(h, host) != 0)
164*63afb9a5SDavid du Colombier 			Bprint(bw, "%s %d %.10M %.10M\n",
165*63afb9a5SDavid du Colombier 				h, mpsignif(k->n), k->ek, k->n);
166*63afb9a5SDavid du Colombier 		free(h);
167*63afb9a5SDavid du Colombier 		rsapubfree(k);
168*63afb9a5SDavid du Colombier 	}
169*63afb9a5SDavid du Colombier 	Bprint(bw, "%s %d %.10M %.10M\n", host, mpsignif(hostkey->n),
170*63afb9a5SDavid du Colombier 		hostkey->ek, hostkey->n);
171*63afb9a5SDavid du Colombier 	Bterm(bw);
172*63afb9a5SDavid du Colombier 	Bterm(br);
173*63afb9a5SDavid du Colombier 
174*63afb9a5SDavid du Colombier 	d = dirstat(nkey);
175*63afb9a5SDavid du Colombier 	if(d == nil){
176*63afb9a5SDavid du Colombier 		fprint(2, "new key file disappeared?\n");
177*63afb9a5SDavid du Colombier 		goto out;
178*63afb9a5SDavid du Colombier 	}
179*63afb9a5SDavid du Colombier 
180*63afb9a5SDavid du Colombier 	p = strrchr(d->name, '.');
181*63afb9a5SDavid du Colombier 	if(p == nil || strcmp(p, ".new") != 0){
182*63afb9a5SDavid du Colombier 		fprint(2, "%s: new key file changed names? %s to %s\n",
183*63afb9a5SDavid du Colombier 			argv0, nkey, d->name);
184*63afb9a5SDavid du Colombier 		goto out;
185*63afb9a5SDavid du Colombier 	}
186*63afb9a5SDavid du Colombier 
187*63afb9a5SDavid du Colombier 	*p = '\0';
188*63afb9a5SDavid du Colombier 	nulldir(&nd);
189*63afb9a5SDavid du Colombier 	nd.name = d->name;
190*63afb9a5SDavid du Colombier 	if(remove(keyfile) < 0){
191*63afb9a5SDavid du Colombier 		fprint(2, "%s: error removing %s: %r\n", argv0, keyfile);
192*63afb9a5SDavid du Colombier 		goto out;
193*63afb9a5SDavid du Colombier 	}
194*63afb9a5SDavid du Colombier 	if(dirwstat(nkey, &nd) < 0){
195*63afb9a5SDavid du Colombier 		fprint(2, "%s: error renaming %s to %s: %r\n",
196*63afb9a5SDavid du Colombier 			argv0, nkey, d->name);
197*63afb9a5SDavid du Colombier 		goto out;
198*63afb9a5SDavid du Colombier 	}
199*63afb9a5SDavid du Colombier 	ret = 0;
200*63afb9a5SDavid du Colombier out:
201*63afb9a5SDavid du Colombier 	free(d);
202*63afb9a5SDavid du Colombier 	free(nkey);
203*63afb9a5SDavid du Colombier 	return ret;
204*63afb9a5SDavid du Colombier }
205*63afb9a5SDavid du Colombier 
206*63afb9a5SDavid du Colombier int
appendkey(char * keyfile,char * host,RSApub * key)207*63afb9a5SDavid du Colombier appendkey(char *keyfile, char *host, RSApub *key)
208*63afb9a5SDavid du Colombier {
209*63afb9a5SDavid du Colombier 	int fd, ret;
210*63afb9a5SDavid du Colombier 
211*63afb9a5SDavid du Colombier 	ret = -1;
212*63afb9a5SDavid du Colombier 	if((fd = open(keyfile, OWRITE)) < 0){
213*63afb9a5SDavid du Colombier 		fd = create(keyfile, OWRITE, 0666);
214*63afb9a5SDavid du Colombier 		if(fd < 0){
215*63afb9a5SDavid du Colombier 			fprint(2, "%s: can't open nor create %s: %r\n",
216*63afb9a5SDavid du Colombier 				argv0, keyfile);
217*63afb9a5SDavid du Colombier 			return -1;
218*63afb9a5SDavid du Colombier 		}
219*63afb9a5SDavid du Colombier 	}
220*63afb9a5SDavid du Colombier 	if(seek(fd, 0, 2) >= 0 &&
221*63afb9a5SDavid du Colombier 	    fprint(fd, "%s %d %.10M %.10M\n", host, mpsignif(key->n),
222*63afb9a5SDavid du Colombier 	     key->ek, key->n) >= 0)
223*63afb9a5SDavid du Colombier 		ret = 0;
224*63afb9a5SDavid du Colombier 	close(fd);
225*63afb9a5SDavid du Colombier 	return ret;
226*63afb9a5SDavid du Colombier }
227