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