chiark / gitweb /
server/admin.c: Remove spurious `ping' in usage message.
[tripe] / server / keymgmt.c
... / ...
CommitLineData
1/* -*-c-*-
2 *
3 * Key loading and storing
4 *
5 * (c) 2001 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of Trivial IP Encryption (TrIPE).
11 *
12 * TrIPE is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU General Public License as published by the Free
14 * Software Foundation; either version 3 of the License, or (at your
15 * option) any later version.
16 *
17 * TrIPE is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with TrIPE. If not, see <https://www.gnu.org/licenses/>.
24 */
25
26/*----- Header files ------------------------------------------------------*/
27
28#include "tripe.h"
29
30/*----- Algswitch stuff ---------------------------------------------------*/
31
32/* --- @algs_get@ --- *
33 *
34 * Arguments: @algswitch *a@ = where to put the algorithms
35 * @dstr *e@ = where to write error tokens
36 * @key_file *kf@ = key file
37 * @key *k@ = key to inspect
38 *
39 * Returns: Zero if OK; nonzero on error.
40 *
41 * Use: Extracts an algorithm choice from a key.
42 */
43
44static int algs_get(algswitch *a, dstr *e, key_file *kf, key *k)
45{
46 const char *p;
47 const bulkops *bops;
48 dstr d = DSTR_INIT, dd = DSTR_INIT;
49 int rc = -1;
50
51 /* --- Hash function --- */
52
53 if ((p = key_getattr(kf, k, "hash")) == 0) p = "rmd160";
54 if ((a->h = ghash_byname(p)) == 0) {
55 a_format(e, "unknown-hash", "%s", p, A_END);
56 goto done;
57 }
58
59 /* --- Symmetric encryption for key derivation --- */
60
61 if ((p = key_getattr(kf, k, "mgf")) == 0) {
62 dstr_reset(&d);
63 dstr_putf(&d, "%s-mgf", a->h->name);
64 p = d.buf;
65 }
66 if ((a->mgf = gcipher_byname(p)) == 0) {
67 a_format(e, "unknown-mgf-cipher", "%s", p, A_END);
68 goto done;
69 }
70
71 /* --- Bulk crypto transform --- */
72
73 if ((p = key_getattr(kf, k, "bulk")) == 0) p = "v0";
74 for (bops = bulktab; bops->name && strcmp(p, bops->name) != 0; bops++);
75 if (!bops->name) {
76 a_format(e, "unknown-bulk-transform", "%s", p, A_END);
77 goto done;
78 }
79 if ((a->bulk = bops->getalgs(a, e, kf, k)) == 0) goto done;
80 a->bulk->ops = bops;
81
82 /* --- All done --- */
83
84 rc = 0;
85done:
86 dstr_destroy(&d);
87 dstr_destroy(&dd);
88 return (rc);
89}
90
91/* --- @algs_check@ --- *
92 *
93 * Arguments: @algswitch *a@ = a choice of algorithms
94 * @dstr *e@ = where to write error tokens
95 * @const dhgrp *grp@ = the group we're working in
96 *
97 * Returns: Zero if OK; nonzero on error.
98 *
99 * Use: Checks an algorithm choice for sensibleness. This also
100 * derives some useful information from the choices, and you
101 * must call this before committing the algorithm selection
102 * for use by @keyset@ functions.
103 */
104
105static int algs_check(algswitch *a, dstr *e, const dhgrp *grp)
106{
107 a->hashsz = a->h->hashsz;
108
109 if (keysz(a->hashsz, a->mgf->keysz) != a->hashsz) {
110 a_format(e, "mgf", "%s", a->mgf->name,
111 "restrictive-key-schedule",
112 A_END);
113 return (-1);
114 }
115
116 if (a->bulk->ops->checkalgs(a->bulk, a, e)) return (-1);
117
118 return (0);
119}
120
121/* --- @km_samealgsp@ --- *
122 *
123 * Arguments: @const kdata *kdx, *kdy@ = two key data objects
124 *
125 * Returns: Nonzero if their two algorithm selections are the same.
126 *
127 * Use: Checks sameness of algorithm selections: used to ensure that
128 * peers are using sensible algorithms.
129 */
130
131int km_samealgsp(const kdata *kdx, const kdata *kdy)
132{
133 const algswitch *a = &kdx->algs, *aa = &kdy->algs;
134
135 return (kdx->grp->ops == kdy->grp->ops &&
136 kdx->grp->ops->samegrpp(kdx->grp, kdy->grp) &&
137 a->mgf == aa->mgf && a->h == aa->h &&
138 a->bulk->ops == aa->bulk->ops &&
139 a->bulk->ops->samealgsp(a->bulk, aa->bulk));
140}
141
142/*----- Key data and key nodes --------------------------------------------*/
143
144typedef struct keyhalf {
145 const char *kind;
146 int (*load)(key_file *, key *, key_data *,
147 const dhops *, kdata *, dstr *, dstr *);
148 char *kr;
149 key_file *kf;
150 fwatch w;
151 sym_table tab;
152} keyhalf;
153
154/* --- @kh_loadpub@, @kh_loadpriv@ --- *
155 *
156 * Arguments: @const dhops *dh@ = Diffie--Hellman operations for key type
157 * @key_file *kf@ = key file from which the key was loaded
158 * @key *k@ = the key object we're loading
159 * @key_data *d@ = the key data to load
160 * @kdata *kd@ = our key-data object to fill in
161 * @dstr *t@ = the key tag name
162 * @dstr *e@ = a string to write error tokens to
163 *
164 * Returns: Zero on success, @-1@ on error.
165 *
166 * Use: These functions handle the main difference between public and
167 * private key halves. They are responsible for setting @grp@,
168 * @k@ and @K@ appropriately in all keys, handling the mismatch
169 * between the largely half-indifferent calling code and the
170 * group-specific loading functions.
171 *
172 * The function @kh_loadpriv@ is also responsible for checking
173 * the group for goodness. We don't bother checking public
174 * keys, because each public key we actually end up using must
175 * share a group with a private key which we'll already have
176 * checked.
177 */
178
179static int kh_loadpub(key_file *kf, key *k, key_data *d,
180 const dhops *dh, kdata *kd, dstr *t, dstr *e)
181{
182 int rc;
183
184 if ((rc = dh->ldpub(kf, k, d, kd, t, e)) != 0)
185 goto fail_0;
186 kd->grp->ops = dh;
187 if (kd->grp->ops->checkge(kd->grp, kd->K)) {
188 a_format(e, "bad-public-group-element", A_END);
189 goto fail_1;
190 }
191 return (0);
192
193fail_1:
194 kd->grp->ops->freege(kd->grp, kd->K);
195 kd->grp->ops->freegrp(kd->grp);
196fail_0:
197 return (-1);
198}
199
200static int kh_loadpriv(key_file *kf, key *k, key_data *d,
201 const dhops *dh, kdata *kd, dstr *t, dstr *e)
202{
203 int rc;
204 const char *err;
205 dhge *K;
206 int ok;
207
208 if ((rc = dh->ldpriv(kf, k, d, kd, t, e)) != 0)
209 goto fail_0;
210 kd->grp->ops = dh;
211 if ((err = kd->grp->ops->checkgrp(kd->grp)) != 0) {
212 a_format(e, "bad-group", "%s", err, A_END);
213 goto fail_1;
214 }
215 K = kd->grp->ops->mul(kd->grp, kd->k, 0);
216 ok = kd->grp->ops->eq(kd->grp, kd->K, K);
217 kd->grp->ops->freege(kd->grp, K);
218 if (!ok) {
219 a_format(e, "incorrect-public-key", A_END);
220 goto fail_1;
221 }
222 return (0);
223
224fail_1:
225 kd->grp->ops->freesc(kd->grp, kd->k);
226 kd->grp->ops->freege(kd->grp, kd->K);
227 kd->grp->ops->freegrp(kd->grp);
228fail_0:
229 return (-1);
230}
231
232static struct keyhalf
233 priv = { "private", kh_loadpriv },
234 pub = { "public", kh_loadpub };
235
236/* --- @keymoan@ --- *
237 *
238 * Arguments: @const char *file@ = name of the file
239 * @int line@ = line number in file
240 * @const char *msg@ = error message
241 * @void *p@ = argument pointer (indicates which keyring)
242 *
243 * Returns: ---
244 *
245 * Use: Reports an error message about loading a key file.
246 */
247
248static void keymoan(const char *file, int line, const char *msg, void *p)
249{
250 keyhalf *kh = p;
251
252 if (!line) {
253 a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", file,
254 "io-error", "?ERRNO", A_END);
255 } else {
256 a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", file, "line", "%d", line,
257 "%s", msg, A_END);
258 }
259}
260
261/* --- @kh_reopen@ --- *
262 *
263 * Arguments: @keyhalf *kh@ = pointer to keyhalf structure
264 *
265 * Returns: Zero on success, @-1@ on error.
266 *
267 * Use: Reopens the key file for the appropriate key half. If this
268 * fails, everything is left as it was; if it succeeds, then the
269 * old file is closed (if it was non-null) and the new one put
270 * in its place.
271 */
272
273static int kh_reopen(keyhalf *kh)
274{
275 key_file *kf = CREATE(key_file);
276
277 if (key_open(kf, kh->kr, KOPEN_READ, keymoan, kh)) {
278 DESTROY(kf);
279 return (-1);
280 }
281 if (kh->kf) {
282 key_close(kh->kf);
283 DESTROY(kh->kf);
284 }
285 kh->kf = kf;
286 return (0);
287}
288
289/* --- @kh_init@ --- *
290 *
291 * Arguments: @keyhalf *kh@ = pointer to keyhalf structure to set up
292 * @const char *kr@ = name of the keyring file
293 *
294 * Returns: Zero on success, @-1@ on error.
295 *
296 * Use: Initialize a keyhalf structure, maintaining the private or
297 * public keys. Intended to be called during initialization:
298 * exits if there's some kind of problem.
299 */
300
301static int kh_init(keyhalf *kh, const char *kr)
302{
303 if (kh->kf) return (0);
304 kh->kr = xstrdup(kr);
305 if (kh_reopen(kh)) return (-1);
306 fwatch_init(&kh->w, kr);
307 sym_create(&kh->tab);
308 return (0);
309}
310
311/* --- @kh_load@ --- *
312 *
313 * Arguments: @keyhalf *kh@ = pointer to keyhalf
314 * @const char *tag@ = key tag to be loaded
315 * @int complainp@ = whether to complain about missing keys
316 *
317 * Returns: Pointer to a @kdata@ structure if successful, or null on
318 * failure.
319 *
320 * Use: Attempts to load a key from the current key file. This
321 * function always reads data from the file: it's used when
322 * there's a cache miss from @kh_find@, and when refreshing the
323 * known keys in @kh_refresh@. The returned kdata has a
324 * reference count of exactly 1, and has no home knode.
325 */
326
327static kdata *kh_load(keyhalf *kh, const char *tag, int complainp)
328{
329 dstr t = DSTR_INIT;
330 dstr e = DSTR_INIT;
331 key *k;
332 key_data **d;
333 kdata *kd;
334 const char *ty;
335 const dhops *dh;
336 T( const dhgrp *g; )
337
338 /* --- Find the key and grab its tag --- */
339
340 if (key_qtag(kh->kf, tag, &t, &k, &d)) {
341 if (complainp) {
342 a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", kh->kr,
343 "key-not-found", "%s", tag, A_END);
344 }
345 goto fail_0;
346 }
347
348 /* --- Find the key's group type and the appropriate operations --- *
349 *
350 * There are several places to look for the key type. The most obvious is
351 * the `kx-group' key attribute. But there's also the key type itself, for
352 * compatibility reasons.
353 */
354
355 ty = key_getattr(kh->kf, k, "kx-group");
356 if (!ty && strncmp(k->type, "tripe-", 6) == 0) ty = k->type + 6;
357 if (!ty) ty = "dh";
358
359 for (dh = dhtab; dh->name; dh++)
360 if (strcmp(dh->name, ty) == 0) goto founddh;
361 a_warn("KEYMGMT", "%s-keyring", kh->kind,
362 "%s", kh->kr, "key", "%s", t.buf,
363 "unknown-group-type", "%s", ty, A_END);
364 goto fail_0;
365
366founddh:
367 kd = CREATE(kdata);
368 if (kh->load(kh->kf, k, *d, dh, kd, &t, &e)) {
369 a_warn("KEYMGMT", "%s-keyring", kh->kind,
370 "%s", kh->kr, "key", "%s", t.buf,
371 "*%s", e.buf, A_END);
372 goto fail_1;
373 }
374
375 if (algs_get(&kd->algs, &e, kh->kf, k) ||
376 algs_check(&kd->algs, &e, kd->grp)) {
377 a_warn("KEYMGMT", "%s-keyring", kh->kind,
378 "%s", kh->kr, "key", "%s", t.buf,
379 "*%s", e.buf, A_END);
380 goto fail_2;
381 }
382
383 kd->tag = xstrdup(t.buf);
384 kd->ref = 1;
385 kd->kn = 0;
386 kd->id = k->id;
387 kd->t_exp = k->exp;
388
389 IF_TRACING(T_KEYMGMT, {
390 trace(T_KEYMGMT, "keymgmt: loaded %s key `%s'", kh->kind, t.buf);
391 IF_TRACING(T_CRYPTO, {
392 g = kd->grp;
393 g->ops->tracegrp(g);
394 if (kd->k)
395 trace(T_CRYPTO, "crypto: k = %s", g->ops->scstr(g, kd->k));
396 trace(T_CRYPTO, "crypto: K = %s", g->ops->gestr(g, kd->K));
397 trace(T_CRYPTO, "crypto: bulk transform = %s",
398 kd->algs.bulk->ops->name);
399 kd->algs.bulk->ops->tracealgs(kd->algs.bulk);
400 })
401 })
402
403 goto done;
404
405fail_2:
406 if (kd->k) kd->grp->ops->freesc(kd->grp, kd->k);
407 kd->grp->ops->freege(kd->grp, kd->K);
408 kd->grp->ops->freegrp(kd->grp);
409fail_1:
410 DESTROY(kd);
411fail_0:
412 kd = 0;
413done:
414 dstr_destroy(&t);
415 dstr_destroy(&e);
416 return (kd);
417}
418
419/* --- @kh_find@ --- *
420 *
421 * Arguments: @keyhalf *kh@ = pointer to the keyhalf
422 * @const char *tag@ = key to be obtained
423 * @int complainp@ = whether to complain about missing keys
424 *
425 * Returns: A pointer to the kdata, or null on error.
426 *
427 * Use: Obtains kdata, maybe from the cache. This won't update a
428 * stale cache entry, though @kh_refresh@ ought to have done
429 * that already. The returned kdata object may be shared with
430 * other users. (One of this function's responsibilities, over
431 * @kh_load@, is to set the home knode of a freshly loaded
432 * kdata.)
433 */
434
435static kdata *kh_find(keyhalf *kh, const char *tag, int complainp)
436{
437 knode *kn;
438 kdata *kd;
439 unsigned f;
440
441 kn = sym_find(&kh->tab, tag, -1, sizeof(knode), &f);
442
443 if (f) {
444 if (kn->f & KNF_BROKEN) {
445 T( if (complainp)
446 trace(T_KEYMGMT, "keymgmt: key `%s' marked as broken", tag); )
447 return (0);
448 }
449
450 kd = kn->kd;
451 if (kd) kd->ref++;
452 T( trace(T_KEYMGMT, "keymgmt: %scache hit for key `%s'",
453 kd ? "" : "negative ", tag); )
454 return (kd);
455 } else {
456 kd = kh_load(kh, tag, complainp);
457 kn->kd = kd;
458 kn->kh = kh;
459 kn->f = 0;
460 if (!kd)
461 kn->f |= KNF_BROKEN;
462 else {
463 kd->kn = kn;
464 kd->ref++;
465 }
466 return (kd);
467 }
468}
469
470/* --- @kh_refresh@ --- *
471 *
472 * Arguments: @keyhalf *kh@ = pointer to the keyhalf
473 *
474 * Returns: Zero if nothing needs to be done; nonzero if peers should
475 * refresh their keys.
476 *
477 * Use: Refreshes cached keys from files.
478 *
479 * Each active knode is examined to see if a new key is
480 * available: the return value is nonzero if any new keys are.
481 * A key is considered new if its algorithms, public key, or
482 * expiry time are/is different.
483 *
484 * Stub knodes (with no kdata attached) are removed, so that a
485 * later retry can succeed if the file has been fixed. (This
486 * doesn't count as a change, since no peers should be relying
487 * on a nonexistent key.)
488 */
489
490static int kh_refresh(keyhalf *kh)
491{
492 knode *kn;
493 kdata *kd;
494 sym_iter i;
495 int changep = 0;
496
497 if (!fwatch_update(&kh->w, kh->kr) || kh_reopen(kh))
498 return (0);
499
500 T( trace(T_KEYMGMT, "keymgmt: rescan %s keyring `%s'", kh->kind, kh->kr); )
501 for (sym_mkiter(&i, &kh->tab); (kn = sym_next(&i)) != 0; ) {
502 if (!kn->kd) {
503 T( trace(T_KEYMGMT, "keymgmt: discard stub entry for key `%s'",
504 SYM_NAME(kn)); )
505 sym_remove(&kh->tab, kn);
506 continue;
507 }
508 if ((kd = kh_load(kh, SYM_NAME(kn), 1)) == 0) {
509 if (!(kn->f & KNF_BROKEN)) {
510 T( trace(T_KEYMGMT, "keymgmt: failed to load new key `%s': "
511 "marking it as broken",
512 SYM_NAME(kn)); )
513 kn->f |= KNF_BROKEN;
514 }
515 continue;
516 }
517 kn->f &= ~KNF_BROKEN;
518 if (kd->t_exp == kn->kd->t_exp &&
519 km_samealgsp(kd, kn->kd) &&
520 kd->grp->ops->eq(kd->grp, kd->K, kn->kd->K)) {
521 T( trace(T_KEYMGMT, "keymgmt: key `%s' unchanged", SYM_NAME(kn)); )
522 continue;
523 }
524 T( trace(T_KEYMGMT, "keymgmt: loaded new version of key `%s'",
525 SYM_NAME(kn)); )
526 km_unref(kn->kd);
527 kd->kn = kn;
528 kn->kd = kd;
529 changep = 1;
530 }
531
532 return (changep);
533}
534
535/* --- @kh_clear@ --- *
536 *
537 * Arguments: @keyhalf *kh@ = pointer to keyhalf structure
538 *
539 * Returns: ---
540 *
541 * Use: Clears out the keyhalf's keyring and flushes the cache.
542 */
543
544static void kh_clear(keyhalf *kh)
545{
546 sym_iter i;
547 knode *kn;
548
549 if (!kh->kf) return;
550 for (sym_mkiter(&i, &kh->tab); (kn = sym_next(&i)) != 0; )
551 if (kn->kd) km_unref(kn->kd);
552 sym_destroy(&kh->tab);
553 key_close(kh->kf);
554 xfree(kh->kr);
555 kh->kf = 0;
556}
557
558/*----- Main code ---------------------------------------------------------*/
559
560char *tag_priv = 0;
561kdata *master = 0;
562
563/* --- @km_init@ --- *
564 *
565 * Arguments: @const char *privkr@ = private keyring file
566 * @const char *pubkr@ = public keyring file
567 * @const char *ptag@ = default private-key tag
568 *
569 * Returns: Zero on success, @-1@ on failure.
570 *
571 * Use: Initializes the key-management machinery, loading the
572 * keyrings and so on.
573 */
574
575int km_init(const char *privkr, const char *pubkr, const char *ptag)
576{
577 const gchash *const *hh;
578 kdata *kd;
579
580 for (hh = ghashtab; *hh; hh++) {
581 if ((*hh)->hashsz > MAXHASHSZ) {
582 a_warn("ABORT", "hash-size-too-large", "hash",
583 "%s", (*hh)->name, "size", "%lu", (*hh)->hashsz,
584 "limit", "%d", MAXHASHSZ, A_END);
585 abort();
586 }
587 }
588
589 if (kh_init(&priv, privkr) || kh_init(&pub, pubkr))
590 return (-1);
591
592 tag_priv = ptag ? xstrdup(ptag) : 0;
593 kh_refresh(&priv);
594
595 if ((kd = km_findpriv(tag_priv)) == 0) return (-1);
596 if (master) km_unref(master);
597 master = kd;
598
599 return (0);
600}
601
602/* --- @km_reload@ --- *
603 *
604 * Arguments: ---
605 *
606 * Returns: Zero if OK, nonzero to force reloading of keys.
607 *
608 * Use: Checks the keyrings to see if they need reloading.
609 */
610
611int km_reload(void)
612{
613 int changep = 0;
614 kdata *kd;
615
616 if (kh_refresh(&priv)) {
617 changep = 1;
618 kd = master->kn->kd;
619 if (kd != master) {
620 km_unref(master);
621 km_ref(kd);
622 master = kd;
623 }
624 }
625 if (kh_refresh(&pub))
626 changep = 1;
627 return (changep);
628}
629
630/* --- @km_clear@ --- *
631 *
632 * Arguments: ---
633 *
634 * Returns: ---
635 *
636 * Use: Forget the currently loaded keyrings. The @master@ key will
637 * be cleared, but other keys already loaded will continue to
638 * exist until their reference count drops to zero. Call
639 * @km_init@ to make everything work again.
640 */
641
642void km_clear(void)
643{
644 kh_clear(&priv);
645 kh_clear(&pub);
646 if (master) { km_unref(master); master = 0; }
647 if (tag_priv) { xfree(tag_priv); tag_priv = 0; }
648}
649
650/* --- @km_findpub@, @km_findpriv@ --- *
651 *
652 * Arguments: @const char *tag@ = key tag to load
653 *
654 * Returns: Pointer to the kdata object if successful, or null on error.
655 *
656 * Use: Fetches a public or private key from the keyring.
657 */
658
659kdata *km_findpub(const char *tag) { return (kh_find(&pub, tag, 1)); }
660
661kdata *km_findpriv(const char *tag)
662{
663 kdata *kd;
664
665 /* Unpleasantness for the sake of compatibility. */
666 if (!tag && (kd = kh_find(&priv, "tripe", 0)) != 0) return (kd);
667 else return (kh_find(&priv, tag ? tag : "tripe-dh", 1));
668}
669
670/* --- @km_findpubbyid@, @km_findprivbyid@ --- *
671 *
672 * Arguments: @uint32 id@ = key id to load
673 *
674 * Returns: Pointer to the kdata object if successful, or null on error.
675 *
676 * Use: Fetches a public or private key from the keyring given its
677 * numeric id.
678 */
679
680static kdata *findbyid(keyhalf *kh, uint32 id)
681{
682 key *k;
683 kdata *kd;
684
685 k = key_byid(kh->kf, id); if (!k) goto notfound;
686 kd = kh_find(kh, k->tag, 1); if (!kd) goto notfound;
687 if (kd->id != id) { km_unref(kd); goto notfound; }
688 return (kd);
689
690notfound:
691 a_warn("KX", "%s-keyring", kh->kind, "%s", kh->kr,
692 "unknown-key-id", "0x%08lx", (unsigned long)id,
693 A_END);
694 return (0);
695}
696
697kdata *km_findpubbyid(uint32 id) { return (findbyid(&pub, id)); }
698
699kdata *km_findprivbyid(uint32 id)
700{
701 if (id == master->id) { km_ref(master); return (master); }
702 else return findbyid(&priv, id);
703}
704
705/* --- @km_tag@ --- *
706 *
707 * Arguments: @kdata *kd@ - pointer to the kdata object
708 *
709 * Returns: A pointer to the short tag by which the kdata was loaded.
710 */
711
712const char *km_tag(kdata *kd) { return (SYM_NAME(kd->kn)); }
713
714/* --- @km_ref@ --- *
715 *
716 * Arguments: @kdata *kd@ = pointer to the kdata object
717 *
718 * Returns: ---
719 *
720 * Use: Claim a new reference to a kdata object.
721 */
722
723void km_ref(kdata *kd) { kd->ref++; }
724
725/* --- @km_unref@ --- *
726 *
727 * Arguments: @kdata *kd@ = pointer to the kdata object
728 *
729 * Returns: ---
730 *
731 * Use: Releases a reference to a kdata object.
732 */
733
734void km_unref(kdata *kd)
735{
736 if (--kd->ref) return;
737 if (kd->k) kd->grp->ops->freesc(kd->grp, kd->k);
738 kd->grp->ops->freege(kd->grp, kd->K);
739 kd->grp->ops->freegrp(kd->grp);
740 xfree(kd->tag);
741 DESTROY(kd);
742}
743
744/*----- That's all, folks -------------------------------------------------*/