1 | /* $Id: mr_sauth.c 3956 2010-01-05 20:56:56Z zacheiss $ |
---|
2 | * |
---|
3 | * Handle server side of authentication |
---|
4 | * |
---|
5 | * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology |
---|
6 | * For copying and distribution information, please see the file |
---|
7 | * <mit-copyright.h>. |
---|
8 | * |
---|
9 | */ |
---|
10 | |
---|
11 | #include <mit-copyright.h> |
---|
12 | #include "mr_server.h" |
---|
13 | |
---|
14 | #include <sys/types.h> |
---|
15 | |
---|
16 | #include <arpa/inet.h> |
---|
17 | #include <netinet/in.h> |
---|
18 | |
---|
19 | #include <stdlib.h> |
---|
20 | #include <string.h> |
---|
21 | |
---|
22 | RCSID("$HeadURL: svn+ssh://svn.mit.edu/moira/trunk/moira/server/mr_sauth.c $ $Id: mr_sauth.c 3956 2010-01-05 20:56:56Z zacheiss $"); |
---|
23 | |
---|
24 | extern char *whoami, *host; |
---|
25 | extern int proxy_acl; |
---|
26 | extern krb5_context context; |
---|
27 | |
---|
28 | static int set_client(client *cl, char *kname, |
---|
29 | char *name, char *inst, char *realm); |
---|
30 | |
---|
31 | #ifdef HAVE_KRB4 |
---|
32 | typedef struct _replay_cache { |
---|
33 | KTEXT_ST auth; |
---|
34 | time_t expires; |
---|
35 | struct _replay_cache *next; |
---|
36 | } replay_cache; |
---|
37 | |
---|
38 | replay_cache *rcache = NULL; |
---|
39 | #endif |
---|
40 | |
---|
41 | /* |
---|
42 | * Handle a MOIRA_AUTH RPC request. |
---|
43 | * |
---|
44 | * argv[0] is a kerberos authenticator. Decompose it, and if |
---|
45 | * successful, store the name the user authenticated to in |
---|
46 | * cl->cl_name. |
---|
47 | */ |
---|
48 | |
---|
49 | void do_auth(client *cl) |
---|
50 | { |
---|
51 | #ifdef HAVE_KRB4 |
---|
52 | KTEXT_ST auth; |
---|
53 | AUTH_DAT ad; |
---|
54 | int status; |
---|
55 | replay_cache *rc, *rcnew; |
---|
56 | time_t now; |
---|
57 | |
---|
58 | auth.length = cl->req.mr_argl[0]; |
---|
59 | memcpy(auth.dat, cl->req.mr_argv[0], auth.length); |
---|
60 | auth.mbz = 0; |
---|
61 | |
---|
62 | if ((status = krb_rd_req(&auth, MOIRA_SNAME, host, |
---|
63 | cl->haddr.sin_addr.s_addr, &ad, ""))) |
---|
64 | { |
---|
65 | status += ERROR_TABLE_BASE_krb; |
---|
66 | client_reply(cl, status); |
---|
67 | com_err(whoami, status, " (authentication failed)"); |
---|
68 | return; |
---|
69 | } |
---|
70 | |
---|
71 | if (!rcache) |
---|
72 | { |
---|
73 | rcache = xmalloc(sizeof(replay_cache)); |
---|
74 | memset(rcache, 0, sizeof(replay_cache)); |
---|
75 | } |
---|
76 | |
---|
77 | /* scan replay cache */ |
---|
78 | for (rc = rcache->next; rc; rc = rc->next) |
---|
79 | { |
---|
80 | if (auth.length == rc->auth.length && |
---|
81 | !memcmp(&(auth.dat), &(rc->auth.dat), auth.length)) |
---|
82 | { |
---|
83 | com_err(whoami, 0, |
---|
84 | "Authenticator replay from %s using authenticator for %s", |
---|
85 | inet_ntoa(cl->haddr.sin_addr), |
---|
86 | mr_kname_unparse(ad.pname, ad.pinst, ad.prealm)); |
---|
87 | com_err(whoami, KE_RD_AP_REPEAT, " (authentication failed)"); |
---|
88 | client_reply(cl, KE_RD_AP_REPEAT); |
---|
89 | return; |
---|
90 | } |
---|
91 | } |
---|
92 | |
---|
93 | /* add new entry */ |
---|
94 | time(&now); |
---|
95 | rcnew = xmalloc(sizeof(replay_cache)); |
---|
96 | memcpy(&(rcnew->auth), &auth, sizeof(KTEXT_ST)); |
---|
97 | rcnew->expires = now + 2 * CLOCK_SKEW; |
---|
98 | rcnew->next = rcache->next; |
---|
99 | rcache->next = rcnew; |
---|
100 | |
---|
101 | /* clean cache */ |
---|
102 | for (rc = rcnew; rc->next; ) |
---|
103 | { |
---|
104 | if (rc->next->expires < now) |
---|
105 | { |
---|
106 | rcnew = rc->next; |
---|
107 | rc->next = rc->next->next; |
---|
108 | free(rcnew); |
---|
109 | } |
---|
110 | else |
---|
111 | rc = rc->next; |
---|
112 | } |
---|
113 | |
---|
114 | status = set_client(cl, mr_kname_unparse(ad.pname, ad.pinst, ad.prealm), |
---|
115 | ad.pname, ad.pinst, ad.prealm); |
---|
116 | |
---|
117 | strncpy(cl->entity, cl->req.mr_argv[1], sizeof(cl->entity) - 1); |
---|
118 | cl->entity[sizeof(cl->entity) - 1] = 0; |
---|
119 | |
---|
120 | memset(&ad, 0, sizeof(ad)); /* Clean up session key, etc. */ |
---|
121 | |
---|
122 | com_err(whoami, 0, "Auth to %s using %s, uid %d cid %d", |
---|
123 | cl->clname, cl->entity, cl->users_id, cl->client_id); |
---|
124 | |
---|
125 | if (status != MR_SUCCESS || cl->users_id != 0) |
---|
126 | client_reply(cl, status); |
---|
127 | else |
---|
128 | client_reply(cl, MR_USER_AUTH); |
---|
129 | #else |
---|
130 | client_reply(cl, MR_NO_KRB4); |
---|
131 | #endif |
---|
132 | } |
---|
133 | |
---|
134 | void do_proxy(client *cl) |
---|
135 | { |
---|
136 | char name[ANAME_SZ] = "\0", inst[INST_SZ] = "\0", realm[REALM_SZ] = "\0"; |
---|
137 | char kname[MAX_K_NAME_SZ]; |
---|
138 | |
---|
139 | if (cl->proxy_id) |
---|
140 | { |
---|
141 | com_err(whoami, MR_PERM, "Cannot re-proxy"); |
---|
142 | client_reply(cl, MR_PERM); |
---|
143 | return; |
---|
144 | } |
---|
145 | |
---|
146 | if (mr_kname_parse(name, inst, realm, cl->req.mr_argv[0]) != 0) |
---|
147 | { |
---|
148 | com_err(whoami, KE_KNAME_FMT, "while parsing proxy name %s", |
---|
149 | cl->req.mr_argv); |
---|
150 | client_reply(cl, KE_KNAME_FMT); |
---|
151 | return; |
---|
152 | } |
---|
153 | |
---|
154 | if (!*realm) |
---|
155 | { |
---|
156 | strcpy(realm, krb_realm); |
---|
157 | sprintf(kname, "%s@%s", cl->req.mr_argv[0], realm); |
---|
158 | } |
---|
159 | else |
---|
160 | strcpy(kname, cl->req.mr_argv[0]); |
---|
161 | |
---|
162 | if (find_member("LIST", proxy_acl, cl)) |
---|
163 | { |
---|
164 | cl->proxy_id = cl->client_id; |
---|
165 | set_client(cl, kname, name, inst, realm); |
---|
166 | com_err(whoami, 0, "Proxy authentication as %s (uid %d cid %d) via %s", |
---|
167 | kname, cl->users_id, cl->client_id, cl->req.mr_argv[1]); |
---|
168 | client_reply(cl, MR_SUCCESS); |
---|
169 | } |
---|
170 | else |
---|
171 | { |
---|
172 | com_err(whoami, MR_PERM, "Proxy authentication denied"); |
---|
173 | client_reply(cl, MR_PERM); |
---|
174 | } |
---|
175 | } |
---|
176 | |
---|
177 | static int set_client(client *cl, char *kname, |
---|
178 | char *name, char *inst, char *realm) |
---|
179 | { |
---|
180 | int ok; |
---|
181 | |
---|
182 | strncpy(cl->clname, kname, sizeof(cl->clname)); |
---|
183 | cl->clname[sizeof(cl->clname) - 1] = '\0'; |
---|
184 | |
---|
185 | if ((!inst || inst[0] == 0) && !strcmp(realm, krb_realm)) |
---|
186 | ok = 1; |
---|
187 | else |
---|
188 | ok = 0; |
---|
189 | /* this is in a separate function because it accesses the database */ |
---|
190 | return set_krb_mapping(cl->clname, name, ok, &cl->client_id, &cl->users_id); |
---|
191 | } |
---|
192 | |
---|
193 | void do_krb5_auth(client *cl) |
---|
194 | { |
---|
195 | krb5_data auth; |
---|
196 | krb5_auth_context auth_con = NULL; |
---|
197 | krb5_principal server = NULL, client = NULL; |
---|
198 | krb5_ticket *ticket; |
---|
199 | char name[ANAME_SZ], inst[INST_SZ], realm[REALM_SZ]; |
---|
200 | int status; |
---|
201 | |
---|
202 | ticket = NULL; |
---|
203 | |
---|
204 | status = krb5_auth_con_init(context, &auth_con); |
---|
205 | if (status) |
---|
206 | { |
---|
207 | client_reply(cl, status); |
---|
208 | com_err(whoami, status, "(krb5 auth context init failed)"); |
---|
209 | goto out; |
---|
210 | } |
---|
211 | |
---|
212 | status = krb5_sname_to_principal(context, host, MOIRA_SNAME, |
---|
213 | KRB5_NT_SRV_HST, &server); |
---|
214 | if (status) |
---|
215 | { |
---|
216 | client_reply(cl, status); |
---|
217 | com_err(whoami, status, "(krb5_sname_to_principal failed)"); |
---|
218 | goto out; |
---|
219 | } |
---|
220 | |
---|
221 | auth.length = cl->req.mr_argl[0]; |
---|
222 | auth.data = cl->req.mr_argv[0]; |
---|
223 | |
---|
224 | status = krb5_rd_req(context, &auth_con, &auth, server, NULL, NULL, |
---|
225 | &ticket); |
---|
226 | if (status) |
---|
227 | { |
---|
228 | client_reply(cl, status); |
---|
229 | com_err(whoami, status, " (krb5 authentication failed)"); |
---|
230 | goto out; |
---|
231 | } |
---|
232 | |
---|
233 | status = krb5_copy_principal(context, ticket->enc_part2->client, &client); |
---|
234 | if (status) |
---|
235 | { |
---|
236 | client_reply(cl, status); |
---|
237 | com_err(whoami, status, " (krb5_copy_principal failed)"); |
---|
238 | goto out; |
---|
239 | } |
---|
240 | |
---|
241 | /* Always convert to krb4 style principal name for now. */ |
---|
242 | status = krb5_524_conv_principal(context, client, name, inst, realm); |
---|
243 | if (status) |
---|
244 | { |
---|
245 | client_reply(cl, status); |
---|
246 | com_err(whoami, status, " (krb5_524_conv_principal failed)"); |
---|
247 | goto out; |
---|
248 | } |
---|
249 | status = set_client(cl, mr_kname_unparse(name, inst, realm), name, inst, |
---|
250 | realm); |
---|
251 | |
---|
252 | strncpy(cl->entity, cl->req.mr_argv[1], sizeof(cl->entity) - 1); |
---|
253 | cl->entity[sizeof(cl->entity) - 1] = 0; |
---|
254 | |
---|
255 | com_err(whoami, 0, "krb5 auth to %s using %s, uid %d cid %d", |
---|
256 | cl->clname, cl->entity, cl->users_id, cl->client_id); |
---|
257 | |
---|
258 | if (status != MR_SUCCESS || cl->users_id != 0) |
---|
259 | client_reply(cl, status); |
---|
260 | else |
---|
261 | client_reply(cl, MR_USER_AUTH); |
---|
262 | |
---|
263 | out: |
---|
264 | if (client) |
---|
265 | krb5_free_principal(context, client); |
---|
266 | if (server) |
---|
267 | krb5_free_principal(context, server); |
---|
268 | if (ticket) |
---|
269 | krb5_free_ticket(context, ticket); |
---|
270 | if (auth_con) |
---|
271 | krb5_auth_con_free(context, auth_con); |
---|
272 | return; |
---|
273 | } |
---|