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