Add ceod
[public/pyceo-broken.git] / src / gss.c
1 #include <string.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <grp.h>
5
6 #include "util.h"
7 #include "gss.h"
8 #include "net.h"
9 #include "strbuf.h"
10
11 static gss_cred_id_t my_creds = GSS_C_NO_CREDENTIAL;
12 static gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
13 static gss_name_t peer_name = GSS_C_NO_NAME;
14 static gss_name_t imported_service = GSS_C_NO_NAME;
15 static gss_OID mech_type = GSS_C_NO_OID;
16 static gss_buffer_desc peer_principal;
17 static char *peer_username;
18 static OM_uint32 ret_flags;
19 static int complete;
20 char service_name[128];
21
22 static void display_status(char *prefix, OM_uint32 code, int type) {
23     OM_uint32 maj_stat, min_stat;
24     gss_buffer_desc msg;
25     OM_uint32 msg_ctx = 0;
26
27     maj_stat = gss_display_status(&min_stat, code, type, GSS_C_NULL_OID,
28                                   &msg_ctx, &msg);
29     logmsg(LOG_ERR, "%s: %s", prefix, (char *)msg.value);
30     gss_release_buffer(&min_stat, &msg);
31
32     while (msg_ctx) {
33         maj_stat = gss_display_status(&min_stat, code, type, GSS_C_NULL_OID,
34                                       &msg_ctx, &msg);
35         logmsg(LOG_ERR, "additional: %s", (char *)msg.value);
36         gss_release_buffer(&min_stat, &msg);
37     }
38 }
39
40 void gss_fatal(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat) {
41     logmsg(LOG_ERR, "fatal: %s", msg);
42     display_status("major", maj_stat, GSS_C_GSS_CODE);
43     display_status("minor", min_stat, GSS_C_MECH_CODE);
44     exit(1);
45 }
46
47 static void import_service(const char *service, const char *hostname) {
48     OM_uint32 maj_stat, min_stat;
49     gss_buffer_desc buf_desc;
50
51     if (snprintf(service_name, sizeof(service_name),
52                  "%s@%s", service, hostname) >= sizeof(service_name))
53         fatal("service name too long");
54
55     buf_desc.value = service_name;
56     buf_desc.length = strlen(service_name);
57
58     maj_stat = gss_import_name(&min_stat, &buf_desc,
59                                GSS_C_NT_HOSTBASED_SERVICE, &imported_service);
60     if (maj_stat != GSS_S_COMPLETE)
61         gss_fatal("gss_import_name", maj_stat, min_stat);
62 }
63
64 static void check_services(OM_uint32 flags) {
65     debug("gss services: %sconf %sinteg %smutual %sreplay %ssequence",
66             flags & GSS_C_CONF_FLAG     ? "+" : "-",
67             flags & GSS_C_INTEG_FLAG    ? "+" : "-",
68             flags & GSS_C_MUTUAL_FLAG   ? "+" : "-",
69             flags & GSS_C_REPLAY_FLAG   ? "+" : "-",
70             flags & GSS_C_SEQUENCE_FLAG ? "+" : "-");
71     if (~flags & GSS_C_CONF_FLAG)
72         fatal("confidentiality service required");
73     if (~flags & GSS_C_INTEG_FLAG)
74         fatal("integrity service required");
75     if (~flags & GSS_C_MUTUAL_FLAG)
76         fatal("mutual authentication required");
77 }
78
79 void server_acquire_creds(const char *service) {
80     OM_uint32 maj_stat, min_stat;
81     OM_uint32 time_rec;
82
83     if (!strlen(fqdn.buf))
84         fatal("empty fqdn");
85
86     import_service(service, fqdn.buf);
87
88     notice("acquiring credentials for %s", service_name);
89
90     maj_stat = gss_acquire_cred(&min_stat, imported_service, GSS_C_INDEFINITE,
91                                 GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &my_creds,
92                                 NULL, &time_rec);
93     if (maj_stat != GSS_S_COMPLETE)
94         gss_fatal("gss_acquire_cred", maj_stat, min_stat);
95
96     if (time_rec != GSS_C_INDEFINITE)
97         fatal("credentials valid for %d seconds (oops)", time_rec);
98 }
99
100 void client_acquire_creds(const char *service, const char *hostname) {
101     import_service(service, hostname);
102 }
103
104 int process_server_token(gss_buffer_t incoming_tok, gss_buffer_t outgoing_tok) {
105     OM_uint32 maj_stat, min_stat;
106     OM_uint32 time_rec;
107     gss_OID name_type;
108
109     if (complete)
110         fatal("unexpected %zd-byte token from peer", incoming_tok->length);
111
112     maj_stat = gss_accept_sec_context(&min_stat, &context_handle, my_creds,
113             incoming_tok, GSS_C_NO_CHANNEL_BINDINGS, &peer_name, &mech_type,
114             outgoing_tok, &ret_flags, &time_rec, NULL);
115     if (maj_stat == GSS_S_COMPLETE) {
116         check_services(ret_flags);
117
118         complete = 1;
119
120         maj_stat = gss_display_name(&min_stat, peer_name, &peer_principal, &name_type);
121         if (maj_stat != GSS_S_COMPLETE)
122             gss_fatal("gss_display_name", maj_stat, min_stat);
123
124         notice("client authenticated as %s", (char *)peer_principal.value);
125         debug("context expires in %d seconds",time_rec);
126
127     } else if (maj_stat != GSS_S_CONTINUE_NEEDED) {
128         gss_fatal("gss_accept_sec_context", maj_stat, min_stat);
129     }
130
131     return complete;
132 }
133
134 int process_client_token(gss_buffer_t incoming_tok, gss_buffer_t outgoing_tok) {
135     OM_uint32 maj_stat, min_stat;
136     OM_uint32 time_rec;
137     gss_OID_desc krb5 = *gss_mech_krb5;
138
139     if (complete)
140         fatal("unexpected token from peer");
141
142     maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &context_handle,
143                                     imported_service, &krb5, GSS_C_MUTUAL_FLAG |
144                                     GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
145                                     GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS,
146                                     incoming_tok, NULL, outgoing_tok, &ret_flags,
147                                     &time_rec);
148     if (maj_stat == GSS_S_COMPLETE) {
149         notice("server authenticated as %s", service_name);
150         notice("context expires in %d seconds", time_rec);
151
152         check_services(ret_flags);
153
154         complete = 1;
155
156     } else if (maj_stat != GSS_S_CONTINUE_NEEDED) {
157         gss_fatal("gss_init_sec_context", maj_stat, min_stat);
158     }
159
160     return complete;
161 }
162
163 int initial_client_token(gss_buffer_t outgoing_tok) {
164     return process_client_token(GSS_C_NO_BUFFER, outgoing_tok);
165 }
166
167 char *client_principal(void) {
168     return complete ? (char *)peer_principal.value : NULL;
169 }
170
171 char *client_username(void) {
172     if (!peer_username) {
173         char *princ = client_principal();
174         if (princ) {
175             peer_username = xstrdup(princ);
176             char *c = strchr(peer_username, '@');
177             if (c)
178                 *c = '\0';
179         }
180     }
181     return peer_username;
182 }
183