summaryrefslogtreecommitdiff
path: root/lib/ZDumpSession.c
blob: 01c4444c7593a09e6568a3b2849858571c822bc5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* This file is part of the Project Athena Zephyr Notification System.
 * It contains source for the session dump and restore function.
 *
 *	Created by:	David Benjamin
 *
 *	$Id$
 *
 *	Copyright (c) 2013 by the Massachusetts Institute of Technology.
 *	For copying and distribution information, see the file
 *	"mit-copyright.h".
 */

#ifndef lint
static const char rcsid_ZDumpSession_c[] = "$Id$";
#endif

#include <internal.h>

#define SESSION_VERSION 1

Code_t
ZDumpSession(char **buffer,
             int *ret_len)
{
#ifdef HAVE_KRB5
    struct _Z_SessionKey *key;
    uint32_t num_keys = 0;
#endif
    char *ptr;
    int len;

    /*
     * We serialize the port number and all keys. All numbers are
     * stored in big-endian. Byte strings are prefixed with a 32-bit
     * length. First field is 16-bit version number. Keys are stored
     * in reverse.
     */

    len = 2 + 2;  /* version, port number */
#ifdef HAVE_KRB5
    len += 4;  /* num_keys */
    for (key = Z_keys_head; key != NULL; key = key->next) {
	num_keys++;
	len += 4 + 4;  /* enctype, length */
	len += key->keyblock->length;  /* contents */
    }
#endif

    *ret_len = len;
    if (!(*buffer = (char *) malloc((unsigned)*ret_len)))
	return (ENOMEM);

    ptr = *buffer;
    *((uint16_t*) ptr) = htons(SESSION_VERSION); ptr += 2;
    *((uint16_t*) ptr) = htons(__Zephyr_port); ptr += 2;
#ifdef HAVE_KRB5
    *((uint32_t *)ptr) = htonl(num_keys); ptr += 4;
    for (key = Z_keys_tail; key != NULL; key = key->prev) {
	*((uint32_t*) ptr) = htonl(key->keyblock->enctype); ptr += 4;
	*((uint32_t*) ptr) = htonl(key->keyblock->length); ptr += 4;
	memcpy(ptr, key->keyblock->contents, key->keyblock->length);
	ptr += key->keyblock->length;
    }
#endif

    return (ZERR_NONE);
}

Code_t
ZLoadSession(char *buffer, int len)
{
#ifdef HAVE_KRB5
    struct _Z_SessionKey *key;
    uint32_t num_keys, keylength;
    krb5_enctype enctype;
    int i;
#endif
    Code_t ret;
    uint16_t version, port;

    if (len < 2) return (EINVAL);
    version = ntohs(*((uint16_t *) buffer)); buffer += 2; len -= 2;
    if (version != SESSION_VERSION)
	return (EINVAL);

    if (len < 2) return (EINVAL);
    port = ntohs(*((uint16_t *) buffer)); buffer += 2; len -= 2;
    if ((ret = ZOpenPort(&port)) != ZERR_NONE)
	return ret;

#ifdef HAVE_KRB5
    if (len < 4) return (EINVAL);
    num_keys = ntohl(*((uint32_t *) buffer)); buffer += 4; len -= 4;

    for (i = 0; i < num_keys; i++) {
	key = (struct _Z_SessionKey *)malloc(sizeof(struct _Z_SessionKey));
	if (!key)
	    return (ENOMEM);
	if (len < 4) {
	    free(key);
	    return (EINVAL);
	}
	enctype = ntohl(*((uint32_t *) buffer)); buffer += 4; len -= 4;
	if (len < 4) {
	    free(key);
	    return (EINVAL);
	}
	keylength = ntohl(*((uint32_t *) buffer)); buffer += 4; len -= 4;
	if (len < keylength) {
	    free(key);
	    return (EINVAL);
	}
	ret = krb5_init_keyblock(Z_krb5_ctx, enctype, keylength, &key->keyblock);
	if (ret) {
	    free(key);
	    return ret;
	}
	memcpy((char *)key->keyblock->contents, buffer, keylength);
	buffer += keylength; len -= keylength;
	/* Just set recent times. It means we might not be able to
	   retire the keys, but that's fine. */
	key->send_time = time(NULL);
	key->first_use = time(NULL);
	/* Prepend to the key list. */
	key->prev = NULL;
	key->next = Z_keys_head;
	if (Z_keys_head)
	    Z_keys_head->prev = key;
	Z_keys_head = key;
	if (!Z_keys_tail)
	    Z_keys_tail = key;
    }
#endif

    if (len)
	return (EINVAL);
    return (ZERR_NONE);
}