df8a618c7c9917f4c36115e76f7df2b9f946df87
[openafs.git] / src / auth / keys.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9 #include <afsconfig.h>
10 #include <afs/param.h>
11
12 #include <roken.h>
13
14 /* Need rx/rx.h to get working assert(), used by LOCK_GLOBAL_MUTEX */
15 #include <rx/rx.h>
16
17 #include <afs/stds.h>
18 #include <afs/pthread_glock.h>
19 #include <afs/afsutil.h>
20
21 #include "cellconfig.h"
22 #include "keys.h"
23 #include "internal.h"
24
25 /* called during opening of config file */
26 int
27 _afsconf_IntGetKeys(struct afsconf_dir *adir)
28 {
29     char tbuffer[256];
30     int fd;
31     struct afsconf_keys *tstr;
32     afs_int32 code;
33
34 #ifdef AFS_NT40_ENV
35     /* NT client config dir has no KeyFile; don't risk attempting open
36      * because there might be a random file of this name if dir is shared.
37      */
38     if (_afsconf_IsClientConfigDirectory(adir->name)) {
39         adir->keystr = ((struct afsconf_keys *)
40                         malloc(sizeof(struct afsconf_keys)));
41         adir->keystr->nkeys = 0;
42         return 0;
43     }
44 #endif /* AFS_NT40_ENV */
45
46     LOCK_GLOBAL_MUTEX;
47     /* compute the key name and other setup */
48     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
49     tstr = (struct afsconf_keys *)malloc(sizeof(struct afsconf_keys));
50     adir->keystr = tstr;
51
52     /* read key file */
53     fd = open(tbuffer, O_RDONLY);
54     if (fd < 0) {
55         tstr->nkeys = 0;
56         UNLOCK_GLOBAL_MUTEX;
57         return 0;
58     }
59     code = read(fd, tstr, sizeof(struct afsconf_keys));
60     close(fd);
61     if (code < sizeof(afs_int32)) {
62         tstr->nkeys = 0;
63         UNLOCK_GLOBAL_MUTEX;
64         return 0;
65     }
66
67     /* convert key structure to host order */
68     tstr->nkeys = ntohl(tstr->nkeys);
69
70     if (code < sizeof(afs_int32) + (tstr->nkeys*sizeof(struct afsconf_key))) {
71         tstr->nkeys = 0;
72         UNLOCK_GLOBAL_MUTEX;
73         return 0;
74     }
75
76     for (fd = 0; fd < tstr->nkeys; fd++)
77         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
78
79     UNLOCK_GLOBAL_MUTEX;
80     return 0;
81 }
82
83 /* get keys structure */
84 int
85 afsconf_GetKeys(struct afsconf_dir *adir, struct afsconf_keys *astr)
86 {
87     afs_int32 code;
88
89     LOCK_GLOBAL_MUTEX;
90     code = _afsconf_Check(adir);
91     if (code) {
92         UNLOCK_GLOBAL_MUTEX;
93         return AFSCONF_FAILURE;
94     }
95     memcpy(astr, adir->keystr, sizeof(struct afsconf_keys));
96     UNLOCK_GLOBAL_MUTEX;
97     return 0;
98 }
99
100 /* get latest key */
101 afs_int32
102 afsconf_GetLatestKey(struct afsconf_dir * adir, afs_int32 * avno,
103                      struct ktc_encryptionKey *akey)
104 {
105     int i;
106     int maxa;
107     struct afsconf_key *tk;
108     afs_int32 best;
109     struct afsconf_key *bestk;
110     afs_int32 code;
111
112     LOCK_GLOBAL_MUTEX;
113     code = _afsconf_Check(adir);
114     if (code) {
115         UNLOCK_GLOBAL_MUTEX;
116         return AFSCONF_FAILURE;
117     }
118     maxa = adir->keystr->nkeys;
119
120     best = -1;                  /* highest kvno we've seen yet */
121     bestk = (struct afsconf_key *)0;    /* ptr to structure providing best */
122     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
123         if (tk->kvno == 999)
124             continue;           /* skip bcrypt keys */
125         if (tk->kvno > best) {
126             best = tk->kvno;
127             bestk = tk;
128         }
129     }
130     if (bestk) {                /* found any  */
131         if (akey)
132             memcpy(akey, bestk->key, 8);        /* copy out latest key */
133         if (avno)
134             *avno = bestk->kvno;        /* and kvno to caller */
135         UNLOCK_GLOBAL_MUTEX;
136         return 0;
137     }
138     UNLOCK_GLOBAL_MUTEX;
139     return AFSCONF_NOTFOUND;    /* didn't find any keys */
140 }
141
142 /* get a particular key */
143 int
144 afsconf_GetKey(void *rock, int avno, struct ktc_encryptionKey *akey)
145 {
146     struct afsconf_dir *adir = (struct afsconf_dir *) rock;
147     int i, maxa;
148     struct afsconf_key *tk;
149     afs_int32 code;
150
151     LOCK_GLOBAL_MUTEX;
152     code = _afsconf_Check(adir);
153     if (code) {
154         UNLOCK_GLOBAL_MUTEX;
155         return AFSCONF_FAILURE;
156     }
157     maxa = adir->keystr->nkeys;
158
159     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
160         if (tk->kvno == avno) {
161             memcpy(akey, tk->key, 8);
162             UNLOCK_GLOBAL_MUTEX;
163             return 0;
164         }
165     }
166
167     UNLOCK_GLOBAL_MUTEX;
168     return AFSCONF_NOTFOUND;
169 }
170
171 /* save the key structure in the appropriate file */
172 static int
173 SaveKeys(struct afsconf_dir *adir)
174 {
175     struct afsconf_keys tkeys;
176     int fd;
177     afs_int32 i;
178     char tbuffer[256];
179
180     memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys));
181
182     /* convert it to net byte order */
183     for (i = 0; i < tkeys.nkeys; i++)
184         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
185     tkeys.nkeys = htonl(tkeys.nkeys);
186
187     /* rewrite keys file */
188     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
189     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
190     if (fd < 0)
191         return AFSCONF_FAILURE;
192     i = write(fd, &tkeys, sizeof(tkeys));
193     if (i != sizeof(tkeys)) {
194         close(fd);
195         return AFSCONF_FAILURE;
196     }
197     if (close(fd) < 0)
198         return AFSCONF_FAILURE;
199     return 0;
200 }
201
202 int
203 afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8],
204                afs_int32 overwrite)
205 {
206     struct afsconf_keys *tk;
207     struct afsconf_key *tkey;
208     afs_int32 i;
209     int foundSlot;
210
211     LOCK_GLOBAL_MUTEX;
212     tk = adir->keystr;
213
214     if (akvno != 999) {
215         if (akvno < 0 || akvno > 255) {
216             UNLOCK_GLOBAL_MUTEX;
217             return ERANGE;
218         }
219     }
220     foundSlot = 0;
221     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
222         if (tkey->kvno == akvno) {
223             if (!overwrite) {
224                 UNLOCK_GLOBAL_MUTEX;
225                 return AFSCONF_KEYINUSE;
226             }
227             foundSlot = 1;
228             break;
229         }
230     }
231     if (!foundSlot) {
232         if (tk->nkeys >= AFSCONF_MAXKEYS) {
233             UNLOCK_GLOBAL_MUTEX;
234             return AFSCONF_FULL;
235         }
236         tkey = &tk->key[tk->nkeys++];
237     }
238     tkey->kvno = akvno;
239     memcpy(tkey->key, akey, 8);
240     i = SaveKeys(adir);
241     _afsconf_Touch(adir);
242     UNLOCK_GLOBAL_MUTEX;
243     return i;
244 }
245
246 /* this proc works by sliding the other guys down, rather than using a funny
247     kvno value, so that callers can count on getting a good key in key[0].
248 */
249 int
250 afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno)
251 {
252     struct afsconf_keys *tk;
253     struct afsconf_key *tkey;
254     int i;
255     int foundFlag = 0;
256
257     LOCK_GLOBAL_MUTEX;
258     tk = adir->keystr;
259
260     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
261         if (tkey->kvno == akvno) {
262             foundFlag = 1;
263             break;
264         }
265     }
266     if (!foundFlag) {
267         UNLOCK_GLOBAL_MUTEX;
268         return AFSCONF_NOTFOUND;
269     }
270
271     /* otherwise slide the others down.  i and tkey point at the guy to delete */
272     for (; i < tk->nkeys - 1; i++, tkey++) {
273         tkey->kvno = (tkey + 1)->kvno;
274         memcpy(tkey->key, (tkey + 1)->key, 8);
275     }
276     tk->nkeys--;
277     i = SaveKeys(adir);
278     _afsconf_Touch(adir);
279     UNLOCK_GLOBAL_MUTEX;
280     return i;
281 }