]>
Commit | Line | Data |
---|---|---|
7ac48069 | 1 | /* $Id$ |
73cf66ba | 2 | * |
7ac48069 | 3 | * Check access to queries |
73cf66ba | 4 | * |
7ac48069 | 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>. | |
73cf66ba | 8 | */ |
9 | ||
73cf66ba | 10 | #include <mit-copyright.h> |
73cf66ba | 11 | #include "mr_server.h" |
7ac48069 | 12 | #include "qrtn.h" |
03c05291 | 13 | #include "query.h" |
7ac48069 | 14 | |
73cf66ba | 15 | #include <ctype.h> |
7ac48069 | 16 | #include <stdlib.h> |
7b12515f | 17 | #include <netdb.h> |
b4b129dd | 18 | #include <sys/types.h> |
19 | #include <netinet/in.h> | |
20 | #include <arpa/nameser.h> | |
7ac48069 | 21 | |
73cf66ba | 22 | EXEC SQL INCLUDE sqlca; |
7ac48069 | 23 | |
24 | RCSID("$Header$"); | |
73cf66ba | 25 | |
26 | extern char *whoami; | |
03c05291 | 27 | extern int dbms_errno, mr_errcode; |
73cf66ba | 28 | |
03c05291 | 29 | EXEC SQL WHENEVER SQLERROR DO dbmserr(); |
73cf66ba | 30 | |
31 | ||
32 | /* Specialized Access Routines */ | |
33 | ||
34 | /* access_user - verify that client name equals specified login name | |
35 | * | |
36 | * - since field validation routines are called first, a users_id is | |
37 | * now in argv[0] instead of the login name. | |
38 | */ | |
39 | ||
5eaef520 | 40 | int access_user(struct query *q, char *argv[], client *cl) |
73cf66ba | 41 | { |
5eaef520 | 42 | if (cl->users_id != *(int *)argv[0]) |
43 | return MR_PERM; | |
44 | else | |
45 | return MR_SUCCESS; | |
73cf66ba | 46 | } |
47 | ||
34b6375c | 48 | int access_update_user(struct query *q, char *argv[], client *cl) |
49 | { | |
50 | EXEC SQL BEGIN DECLARE SECTION; | |
51 | int users_id, unix_uid, status, comments, secure; | |
52 | char login[USERS_LOGIN_SIZE], shell[USERS_SHELL_SIZE]; | |
53 | char winconsoleshell[USERS_WINCONSOLESHELL_SIZE], last[USERS_LAST_SIZE]; | |
54 | char first[USERS_FIRST_SIZE], middle[USERS_MIDDLE_SIZE]; | |
55 | char clearid[USERS_CLEARID_SIZE], type[USERS_TYPE_SIZE]; | |
56 | char signature[USERS_SIGNATURE_SIZE]; | |
57 | EXEC SQL END DECLARE SECTION; | |
73cf66ba | 58 | |
34b6375c | 59 | /* The two fields we let users update themselves didn't appear until |
60 | * version 11. | |
61 | */ | |
62 | if (q->version < 11) | |
63 | return MR_PERM; | |
64 | ||
65 | if (cl->users_id != *(int *)argv[0]) | |
66 | return MR_PERM; | |
67 | ||
68 | users_id = *(int *)argv[0]; | |
69 | ||
70 | EXEC SQL SELECT u.login, u.unix_uid, u.shell, u.winconsoleshell, u.last, | |
71 | u.first, u.middle, u.status, u.clearid, u.type, u.comments, u.signature, | |
72 | u.secure INTO :login, :unix_uid, :shell, :winconsoleshell, :last, :first, | |
73 | :middle, :status, :clearid, :type, :comments, :signature, :secure | |
74 | FROM USERS u WHERE u.users_id = :users_id; | |
75 | ||
76 | /* None of these things can have changed. */ | |
77 | if (strcmp(argv[1], strtrim(login)) || | |
78 | (unix_uid != atoi(argv[2])) || | |
79 | strcmp(argv[3], strtrim(shell)) || | |
80 | strcmp(argv[4], strtrim(winconsoleshell)) || | |
81 | strcmp(argv[5], strtrim(last)) || | |
82 | strcmp(argv[6], strtrim(first)) || | |
83 | strcmp(argv[7], strtrim(middle)) || | |
84 | (status != atoi(argv[8])) || | |
85 | strcmp(argv[9], strtrim(clearid)) || | |
86 | strcmp(argv[10], strtrim(type)) || | |
87 | (comments != *(int *)argv[11]) || | |
88 | strcmp(argv[12], strtrim(signature)) || | |
89 | (secure != atoi(argv[13]))) | |
90 | return MR_PERM; | |
91 | ||
92 | return MR_SUCCESS; | |
93 | } | |
73cf66ba | 94 | |
95 | /* access_login - verify that client name equals specified login name | |
96 | * | |
97 | * argv[0...n] contain search info. q-> | |
98 | */ | |
99 | ||
5eaef520 | 100 | int access_login(struct query *q, char *argv[], client *cl) |
73cf66ba | 101 | { |
5eaef520 | 102 | EXEC SQL BEGIN DECLARE SECTION; |
103 | int id; | |
104 | EXEC SQL END DECLARE SECTION; | |
73cf66ba | 105 | |
5eaef520 | 106 | if (q->argc != 1) |
107 | return MR_ARGS; | |
03c05291 | 108 | |
5eaef520 | 109 | if (!strcmp(q->shortname, "gual")) |
110 | { | |
5906749e | 111 | EXEC SQL SELECT users_id INTO :id FROM users |
cc0ec904 | 112 | WHERE login = :argv[0] AND users_id != 0; |
5eaef520 | 113 | } |
114 | else if (!strcmp(q->shortname, "gubl")) | |
115 | { | |
03c05291 | 116 | EXEC SQL SELECT users_id INTO :id FROM users u |
cc0ec904 | 117 | WHERE u.login = :argv[0] AND u.users_id != 0; |
5eaef520 | 118 | } |
119 | else if (!strcmp(q->shortname, "guau")) | |
120 | { | |
5906749e | 121 | EXEC SQL SELECT users_id INTO :id FROM users |
122 | WHERE unix_uid = :argv[0] AND users_id != 0; | |
5eaef520 | 123 | } |
124 | else if (!strcmp(q->shortname, "gubu")) | |
125 | { | |
03c05291 | 126 | EXEC SQL SELECT users_id INTO :id FROM users u |
127 | WHERE u.unix_uid = :argv[0] AND u.users_id != 0; | |
73cf66ba | 128 | } |
129 | ||
5eaef520 | 130 | if (sqlca.sqlcode == SQL_NO_MATCH) |
131 | return MR_NO_MATCH; /* ought to be MR_USER, but this is what | |
132 | gual returns, so we have to be consistent */ | |
133 | else if (sqlca.sqlerrd[2] != 1 || id != cl->users_id) | |
134 | return MR_PERM; | |
135 | else | |
136 | return MR_SUCCESS; | |
73cf66ba | 137 | } |
138 | ||
139 | ||
f659afb2 | 140 | /* access_spob - check access for set_pobox */ |
141 | ||
142 | int access_spob(struct query *q, char *argv[], client *cl) | |
143 | { | |
51e578b6 | 144 | EXEC SQL BEGIN DECLARE SECTION; |
145 | int id; | |
146 | EXEC SQL END DECLARE SECTION; | |
7b12515f | 147 | int status; |
51e578b6 | 148 | |
149 | if (!strcmp(argv[1], "IMAP")) | |
22bbb6f0 | 150 | { |
151 | EXEC SQL SELECT owner INTO :id FROM filesys f | |
152 | WHERE f.label = :argv[2] AND f.type = 'IMAP' AND | |
153 | f.lockertype = 'USER'; | |
154 | if (cl->users_id != id) | |
155 | return MR_PERM; | |
156 | } | |
7b12515f | 157 | |
158 | /* Non-query owners can't forward mail to a POSTOFFICE or MAILHUB server, | |
159 | * nor to a nonresolving domain. | |
160 | */ | |
161 | if (!strcmp(argv[1], "SMTP") || !strcmp(argv[1], "SPLIT")) | |
162 | { | |
163 | status = check_mail_string(argv[2]); | |
164 | if (status) | |
165 | return status; | |
166 | } | |
22bbb6f0 | 167 | |
51e578b6 | 168 | if (cl->users_id != *(int *)argv[0]) |
f659afb2 | 169 | return MR_PERM; |
170 | else | |
171 | return MR_SUCCESS; | |
172 | } | |
173 | ||
73cf66ba | 174 | |
175 | /* access_list - check access for most list operations | |
176 | * | |
177 | * Inputs: argv[0] - list_id | |
178 | * q - query name | |
179 | * argv[2] - member ID (only for queries "amtl" and "dmfl") | |
e688520a | 180 | * argv[7] - group ID (only for query "ulis") |
73cf66ba | 181 | * cl - client name |
182 | * | |
183 | * - check that client is a member of the access control list | |
184 | * - OR, if the query is add_member_to_list or delete_member_from_list | |
185 | * and the list is public, allow access if client = member | |
186 | */ | |
187 | ||
5eaef520 | 188 | int access_list(struct query *q, char *argv[], client *cl) |
73cf66ba | 189 | { |
5eaef520 | 190 | EXEC SQL BEGIN DECLARE SECTION; |
40b33762 | 191 | int list_id, acl_id, flags, gid, users_id, member_id, member_acl_id; |
8e3761a2 | 192 | int memacl_id, mailman, mailman_id; |
e688520a | 193 | char acl_type[LIST_ACL_TYPE_SIZE], name[LIST_NAME_SIZE], *newname; |
59c3208b | 194 | char member_acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE]; |
5eaef520 | 195 | EXEC SQL END DECLARE SECTION; |
8e3761a2 | 196 | int status, cnt; |
7b12515f | 197 | char *buf; |
5eaef520 | 198 | |
199 | list_id = *(int *)argv[0]; | |
40b33762 | 200 | member_id = *(int *)argv[2]; |
59c3208b | 201 | EXEC SQL SELECT acl_id, acl_type, memacl_id, memacl_type, |
8e3761a2 | 202 | gid, publicflg, name, mailman, mailman_id |
59c3208b | 203 | INTO :acl_id, :acl_type, :memacl_id, :memacl_type, |
8e3761a2 | 204 | :gid, :flags, :name, :mailman, :mailman_id |
5eaef520 | 205 | FROM list |
206 | WHERE list_id = :list_id; | |
207 | ||
208 | if (sqlca.sqlerrd[2] != 1) | |
209 | return MR_INTERNAL; | |
210 | ||
59c3208b | 211 | /* if update_list, don't allow them to change the GID or rename to a |
212 | username other than their own */ | |
213 | if (!strcmp("ulis", q->shortname)) | |
5eaef520 | 214 | { |
215 | if (!strcmp(argv[7], UNIQUE_GID)) | |
216 | { | |
217 | if (gid != -1) | |
218 | return MR_PERM; | |
219 | } | |
220 | else | |
221 | { | |
222 | if (gid != atoi(argv[7])) | |
223 | return MR_PERM; | |
224 | } | |
5c96d2cd | 225 | |
d83ee5a2 | 226 | newname = argv[1]; |
5c96d2cd | 227 | |
8e3761a2 | 228 | /* Check that it doesn't conflict with the Grouper namespace. */ |
229 | if (strlen(newname) > 4 && isdigit(newname[2]) && | |
230 | isdigit(newname[3]) && newname[4] == '-') | |
231 | { | |
232 | if (!strncasecmp(newname, "fa", 2) || | |
233 | !strncasecmp(newname, "sp", 2) || | |
234 | !strncasecmp(newname, "su", 2) || | |
235 | !strncasecmp(newname, "ja", 2)) | |
236 | return MR_RESERVED; | |
237 | } | |
238 | ||
239 | /* Don't let anyone take owner-foo list names. They interact | |
240 | * weirdly with the aliases automatically generated by | |
241 | * mailhub.gen. | |
242 | */ | |
243 | if (!strncasecmp(newname, "owner-", 6)) | |
244 | return MR_RESERVED; | |
245 | ||
d83ee5a2 | 246 | EXEC SQL SELECT users_id INTO :users_id FROM users |
5eaef520 | 247 | WHERE login = :newname; |
17f090e1 | 248 | if ((sqlca.sqlcode != SQL_NO_MATCH) && strcmp(strtrim(name), newname) && |
249 | (users_id != cl->users_id)) | |
5eaef520 | 250 | return MR_PERM; |
8e3761a2 | 251 | |
252 | /* For modern enough clients, don't allow ordinary users to toggle | |
253 | * the mailman bit or change the server. | |
254 | */ | |
255 | if (q->version >= 10) | |
256 | { | |
257 | if (mailman != atoi(argv[9])) | |
258 | return MR_PERM; | |
259 | ||
260 | if (mailman_id != *(int *)argv[10]) | |
261 | return MR_PERM; | |
262 | } | |
73cf66ba | 263 | } |
264 | ||
7b12515f | 265 | /* Don't allow non-query owners to add STRINGs to lists if they end |
266 | * in a domain that's MIT.EDU or one of the hosts that provide the | |
267 | * MAILHUB or POSTOFFICE services. | |
268 | */ | |
269 | if (!strcmp(q->shortname, "amtl") || !strcmp(q->shortname, "atml")) | |
270 | { | |
271 | if (!strcmp("STRING", argv[1])) | |
272 | { | |
273 | buf = malloc(0); | |
274 | status = id_to_name(*(int *)argv[2], STRINGS_TABLE, &buf); | |
275 | if (status) | |
276 | return status; | |
277 | ||
278 | status = check_mail_string(buf); | |
279 | free(buf); | |
280 | if (status) | |
281 | return status; | |
282 | } | |
283 | } | |
284 | ||
59c3208b | 285 | /* check for client in access control list and return success right |
286 | * away if it's there. */ | |
287 | if (find_member(acl_type, acl_id, cl)) | |
288 | return MR_SUCCESS; | |
289 | ||
290 | /* If not amtl, atml, or dmfl, we lose. */ | |
291 | if (strcmp(q->shortname, "amtl") && strcmp(q->shortname, "atml") && | |
e3fbe284 | 292 | strcmp(q->shortname, "dmfl") && strcmp(q->shortname, "tmol")) |
5eaef520 | 293 | return MR_PERM; |
73cf66ba | 294 | |
59c3208b | 295 | if (find_member(memacl_type, memacl_id, cl)) |
296 | return MR_SUCCESS; | |
297 | ||
b9f9ab1c | 298 | if (flags || q->type == MR_Q_DELETE) |
59c3208b | 299 | { |
300 | if (!strcmp("USER", argv[1]) && *(int *)argv[2] == cl->users_id) | |
301 | return MR_SUCCESS; | |
302 | if (!strcmp("KERBEROS", argv[1]) && *(int *)argv[2] == -cl->client_id) | |
303 | return MR_SUCCESS; | |
304 | if (!strcmp("LIST", argv[1]) && !strcmp("dmfl", q->shortname)) | |
305 | { | |
306 | EXEC SQL SELECT acl_id, acl_type INTO :member_acl_id, | |
307 | :member_acl_type | |
308 | FROM list | |
309 | WHERE list_id = :member_id; | |
310 | ||
311 | if (find_member(member_acl_type, member_acl_id, cl)) | |
312 | return MR_SUCCESS; | |
313 | } | |
314 | } | |
315 | ||
316 | /* Otherwise fail. */ | |
317 | return MR_PERM; | |
73cf66ba | 318 | } |
319 | ||
320 | ||
321 | /* access_visible_list - allow access to list only if it is not hidden, | |
322 | * or if the client is on the ACL | |
323 | * | |
324 | * Inputs: argv[0] - list_id | |
325 | * cl - client identifier | |
326 | */ | |
327 | ||
5eaef520 | 328 | int access_visible_list(struct query *q, char *argv[], client *cl) |
73cf66ba | 329 | { |
5eaef520 | 330 | EXEC SQL BEGIN DECLARE SECTION; |
59c3208b | 331 | int list_id, acl_id, memacl_id, flags ; |
332 | char acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE]; | |
5eaef520 | 333 | EXEC SQL END DECLARE SECTION; |
334 | int status; | |
335 | ||
336 | list_id = *(int *)argv[0]; | |
59c3208b | 337 | EXEC SQL SELECT hidden, acl_id, acl_type, memacl_id, memacl_type |
338 | INTO :flags, :acl_id, :acl_type, :memacl_id, :memacl_type | |
5eaef520 | 339 | FROM list |
340 | WHERE list_id = :list_id; | |
341 | if (sqlca.sqlerrd[2] != 1) | |
342 | return MR_INTERNAL; | |
343 | if (!flags) | |
344 | return MR_SUCCESS; | |
345 | ||
346 | /* check for client in access control list */ | |
347 | status = find_member(acl_type, acl_id, cl); | |
348 | if (!status) | |
59c3208b | 349 | { |
350 | status = find_member(memacl_type, memacl_id, cl); | |
351 | if (!status) | |
352 | return MR_PERM; | |
353 | } | |
5eaef520 | 354 | return MR_SUCCESS; |
73cf66ba | 355 | } |
356 | ||
357 | ||
358 | /* access_vis_list_by_name - allow access to list only if it is not hidden, | |
359 | * or if the client is on the ACL | |
360 | * | |
361 | * Inputs: argv[0] - list name | |
362 | * cl - client identifier | |
363 | */ | |
364 | ||
5eaef520 | 365 | int access_vis_list_by_name(struct query *q, char *argv[], client *cl) |
73cf66ba | 366 | { |
5eaef520 | 367 | EXEC SQL BEGIN DECLARE SECTION; |
2affa9d4 | 368 | int acl_id, memacl_id, flags, rowcount, list_id; |
59c3208b | 369 | char acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE]; |
370 | char *listname; | |
5eaef520 | 371 | EXEC SQL END DECLARE SECTION; |
372 | int status; | |
373 | ||
374 | listname = argv[0]; | |
2affa9d4 | 375 | EXEC SQL SELECT hidden, acl_id, acl_type, memacl_id, memacl_type, list_id |
376 | INTO :flags, :acl_id, :acl_type, :memacl_id, :memacl_type, :list_id | |
59c3208b | 377 | FROM list |
378 | WHERE name = :listname; | |
5eaef520 | 379 | |
380 | rowcount = sqlca.sqlerrd[2]; | |
381 | if (rowcount > 1) | |
382 | return MR_WILDCARD; | |
383 | if (rowcount == 0) | |
384 | return MR_NO_MATCH; | |
385 | if (!flags) | |
386 | return MR_SUCCESS; | |
387 | ||
2affa9d4 | 388 | /* If the user is a member of the acl, memacl, or the list itself, |
389 | * accept them. | |
390 | */ | |
5eaef520 | 391 | status = find_member(acl_type, acl_id, cl); |
392 | if (!status) | |
2affa9d4 | 393 | status = find_member(memacl_type, memacl_id, cl); |
394 | if (!status) | |
395 | status = find_member("LIST", list_id, cl); | |
396 | if (!status) | |
397 | return MR_PERM; | |
398 | ||
5eaef520 | 399 | return MR_SUCCESS; |
73cf66ba | 400 | } |
401 | ||
402 | ||
403 | /* access_member - allow user to access member of type "USER" and name matches | |
f3c08a60 | 404 | * username, or to access member of type "KERBEROS" and the principal matches |
405 | * the user, or to access member of type "LIST" and list is one that user is | |
d1d964a0 | 406 | * on the acl of, or the list is visible. Allow anyone to look up list |
407 | * memberships of MACHINEs. | |
73cf66ba | 408 | */ |
409 | ||
5eaef520 | 410 | int access_member(struct query *q, char *argv[], client *cl) |
73cf66ba | 411 | { |
5eaef520 | 412 | if (!strcmp(argv[0], "LIST") || !strcmp(argv[0], "RLIST")) |
413 | return access_visible_list(q, &argv[1], cl); | |
73cf66ba | 414 | |
5eaef520 | 415 | if (!strcmp(argv[0], "USER") || !strcmp(argv[0], "RUSER")) |
416 | { | |
417 | if (cl->users_id == *(int *)argv[1]) | |
418 | return MR_SUCCESS; | |
73cf66ba | 419 | } |
420 | ||
5eaef520 | 421 | if (!strcmp(argv[0], "KERBEROS") || !strcmp(argv[0], "RKERBEROS")) |
422 | { | |
423 | if (cl->client_id == -*(int *)argv[1]) | |
424 | return MR_SUCCESS; | |
73cf66ba | 425 | } |
426 | ||
d1d964a0 | 427 | if (!strcmp(argv[0], "MACHINE") || !strcmp(argv[0], "RMACHINE")) |
428 | return MR_SUCCESS; | |
429 | ||
5eaef520 | 430 | return MR_PERM; |
73cf66ba | 431 | } |
432 | ||
433 | ||
434 | /* access_qgli - special access routine for Qualified_get_lists. Allows | |
435 | * access iff argv[0] == "TRUE" and argv[2] == "FALSE". | |
436 | */ | |
437 | ||
5eaef520 | 438 | int access_qgli(struct query *q, char *argv[], client *cl) |
73cf66ba | 439 | { |
5eaef520 | 440 | if (!strcmp(argv[0], "TRUE") && !strcmp(argv[2], "FALSE")) |
441 | return MR_SUCCESS; | |
442 | return MR_PERM; | |
73cf66ba | 443 | } |
444 | ||
445 | ||
446 | /* access_service - allow access if user is on ACL of service. Don't | |
447 | * allow access if a wildcard is used. | |
448 | */ | |
449 | ||
5eaef520 | 450 | int access_service(struct query *q, char *argv[], client *cl) |
73cf66ba | 451 | { |
5eaef520 | 452 | EXEC SQL BEGIN DECLARE SECTION; |
453 | int acl_id; | |
e688520a | 454 | char *name, acl_type[LIST_ACL_TYPE_SIZE]; |
5eaef520 | 455 | EXEC SQL END DECLARE SECTION; |
456 | int status; | |
457 | char *c; | |
458 | ||
459 | name = argv[0]; | |
460 | for (c = name; *c; c++) | |
461 | { | |
462 | if (islower(*c)) | |
463 | *c = toupper(*c); | |
464 | } | |
465 | EXEC SQL SELECT acl_id, acl_type INTO :acl_id, :acl_type FROM servers | |
466 | WHERE name = :name; | |
467 | if (sqlca.sqlerrd[2] > 1) | |
468 | return MR_PERM; | |
469 | ||
470 | /* check for client in access control list */ | |
471 | status = find_member(acl_type, acl_id, cl); | |
472 | if (!status) | |
473 | return MR_PERM; | |
474 | ||
475 | return MR_SUCCESS; | |
73cf66ba | 476 | } |
477 | ||
478 | ||
479 | /* access_filesys - verify that client is owner or on owners list of filesystem | |
480 | * named by argv[0] | |
481 | */ | |
482 | ||
5eaef520 | 483 | int access_filesys(struct query *q, char *argv[], client *cl) |
73cf66ba | 484 | { |
5eaef520 | 485 | EXEC SQL BEGIN DECLARE SECTION; |
486 | int users_id, list_id; | |
487 | char *name; | |
488 | EXEC SQL END DECLARE SECTION; | |
489 | int status; | |
490 | ||
491 | name = argv[0]; | |
492 | EXEC SQL SELECT owner, owners INTO :users_id, :list_id FROM filesys | |
493 | WHERE label = :name; | |
494 | ||
495 | if (sqlca.sqlerrd[2] != 1) | |
496 | return MR_PERM; | |
497 | if (users_id == cl->users_id) | |
498 | return MR_SUCCESS; | |
499 | status = find_member("LIST", list_id, cl); | |
500 | if (status) | |
501 | return MR_SUCCESS; | |
502 | else | |
503 | return MR_PERM; | |
73cf66ba | 504 | } |
505 | ||
1beb5e23 | 506 | |
507 | /* access_host - successful if owner of host, or subnet containing host | |
508 | */ | |
509 | ||
5eaef520 | 510 | int access_host(struct query *q, char *argv[], client *cl) |
1beb5e23 | 511 | { |
5eaef520 | 512 | EXEC SQL BEGIN DECLARE SECTION; |
4f6b1a05 | 513 | int mid, sid, id, subnet_status; |
e688520a | 514 | char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE]; |
4f6b1a05 | 515 | char *account_number; |
5eaef520 | 516 | EXEC SQL END DECLARE SECTION; |
e4ae0190 | 517 | int status, idx; |
518 | ||
519 | if (q->version < 6) | |
520 | idx = 0; | |
4f6b1a05 | 521 | else if (q->version >= 6 && q->version < 8) |
e4ae0190 | 522 | idx = 1; |
4f6b1a05 | 523 | else |
524 | idx = 2; | |
525 | ||
b9f9ab1c | 526 | if (q->type == MR_Q_APPEND) |
5eaef520 | 527 | { |
17f090e1 | 528 | /* Non-query owner must set use to zero */ |
e4ae0190 | 529 | if (atoi(argv[6 + idx]) != 0) |
17f090e1 | 530 | return MR_PERM; |
531 | ||
9e252e6f | 532 | /* ... and start the hostname with a letter */ |
533 | if (isdigit(argv[0][0])) | |
534 | return MR_BAD_CHAR; | |
535 | ||
e4ae0190 | 536 | id = *(int *)argv[8 + idx]; |
4f6b1a05 | 537 | EXEC SQL SELECT s.owner_type, s.owner_id, s.status |
538 | INTO :stype, :sid, :subnet_status FROM subnet s | |
5eaef520 | 539 | WHERE s.snet_id = :id; |
540 | mid = 0; | |
17f090e1 | 541 | |
4f6b1a05 | 542 | /* Non query owner must provide valid billing information. */ |
543 | if (q->version >= 8) | |
544 | { | |
545 | if (subnet_status == SNET_STATUS_BILLABLE) | |
546 | { | |
547 | account_number = argv[7]; | |
548 | EXEC SQL SELECT account_number FROM accountnumbers | |
549 | WHERE account_number = :account_number; | |
550 | if (sqlca.sqlcode == SQL_NO_MATCH) | |
551 | return MR_ACCOUNT_NUMBER; | |
552 | } | |
553 | } | |
554 | ||
17f090e1 | 555 | if (find_member(stype, sid, cl)) |
5eaef520 | 556 | return MR_SUCCESS; |
557 | else | |
558 | return MR_PERM; | |
559 | } | |
b9f9ab1c | 560 | else /* q-type == MR_Q_UPDATE */ |
5eaef520 | 561 | { |
17f090e1 | 562 | EXEC SQL BEGIN DECLARE SECTION; |
563 | int status, acomment, use, ocomment, snid; | |
e688520a | 564 | char contact[MACHINE_CONTACT_SIZE], address[MACHINE_ADDRESS_SIZE]; |
9e252e6f | 565 | char name[MACHINE_NAME_SIZE]; |
e4ae0190 | 566 | char billing_contact[MACHINE_BILLING_CONTACT_SIZE]; |
17f090e1 | 567 | EXEC SQL END DECLARE SECTION; |
568 | ||
5eaef520 | 569 | id = *(int *)argv[0]; |
e4ae0190 | 570 | EXEC SQL SELECT m.name, m.use, m.contact, m.billing_contact, m.status, |
571 | m.address, m.owner_type, m.owner_id, m.acomment, m.ocomment, m.snet_id, | |
4f6b1a05 | 572 | s.owner_type, s.owner_id, s.status INTO :name, :use, :contact, |
573 | :billing_contact, :status, :address, :mtype, :mid, :acomment, | |
574 | :ocomment, :snid, :stype, :sid, :subnet_status | |
17f090e1 | 575 | FROM machine m, subnet s |
576 | WHERE m.mach_id = :id AND s.snet_id = m.snet_id; | |
577 | if (dbms_errno) | |
578 | return mr_errcode; | |
579 | ||
4f6b1a05 | 580 | /* Non query owner must provide valid billing information. */ |
581 | if (q->version >= 8) | |
582 | { | |
c862fa4e | 583 | if ((subnet_status == SNET_STATUS_BILLABLE) && |
584 | (atoi(argv[10]) != 3)) | |
4f6b1a05 | 585 | { |
586 | account_number = argv[8]; | |
587 | EXEC SQL SELECT account_number FROM accountnumbers | |
588 | WHERE account_number = :account_number; | |
589 | if (sqlca.sqlcode == SQL_NO_MATCH) | |
590 | return MR_ACCOUNT_NUMBER; | |
591 | } | |
592 | } | |
593 | ||
17f090e1 | 594 | /* non-query-owner cannot change use or ocomment */ |
e4ae0190 | 595 | if ((use != atoi(argv[7 + idx])) || (ocomment != *(int *)argv[14 + idx])) |
17f090e1 | 596 | return MR_PERM; |
597 | ||
9e252e6f | 598 | /* or rename to start with digit */ |
599 | if (isdigit(argv[1][0]) && strcmp(strtrim(name), argv[1])) | |
600 | return MR_BAD_CHAR; | |
601 | ||
17f090e1 | 602 | if (!find_member(stype, sid, cl)) |
603 | { | |
604 | if (find_member(mtype, mid, cl)) | |
605 | { | |
606 | /* host owner also cannot change contact, status, address, | |
607 | owner, or acomment */ | |
7617c632 | 608 | if (strcmp(argv[6], strtrim(contact)) || |
e4ae0190 | 609 | (status != atoi(argv[8 + idx])) || |
610 | strcmp(argv[10 + idx], strtrim(address)) || | |
611 | strcmp(argv[11 + idx], strtrim(mtype)) || | |
612 | (mid != *(int *)argv[12 + idx]) || | |
613 | (acomment != *(int *)argv[13 + idx])) | |
17f090e1 | 614 | return MR_PERM; |
e4ae0190 | 615 | /* Billing contact field didn't appear until version 6 */ |
616 | if (q->version >= 6) | |
7617c632 | 617 | if (strcmp(argv[7], strtrim(billing_contact))) |
e4ae0190 | 618 | return MR_PERM; |
17f090e1 | 619 | } |
620 | else | |
621 | return MR_PERM; | |
622 | } | |
623 | ||
624 | /* If moving to a new subnet, make sure user is on acl there */ | |
e4ae0190 | 625 | id = *(int *)argv[9 + idx]; |
17f090e1 | 626 | if (id != snid) |
627 | { | |
628 | EXEC SQL SELECT owner_type, owner_id INTO :stype, :sid | |
629 | FROM subnet WHERE snet_id=:id; | |
630 | if (!find_member(stype, sid, cl)) | |
631 | return MR_PERM; | |
632 | } | |
5eaef520 | 633 | |
5eaef520 | 634 | return MR_SUCCESS; |
1beb5e23 | 635 | } |
1beb5e23 | 636 | } |
637 | ||
638 | ||
639 | /* access_ahal - check for adding a host alias. | |
640 | * successful if host has less then 2 aliases and (client is owner of | |
641 | * host or subnet). | |
642 | * If deleting an alias, any owner will do. | |
643 | */ | |
644 | ||
5eaef520 | 645 | int access_ahal(struct query *q, char *argv[], client *cl) |
1beb5e23 | 646 | { |
5eaef520 | 647 | EXEC SQL BEGIN DECLARE SECTION; |
648 | int cnt, id, mid, sid; | |
e688520a | 649 | char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE]; |
5eaef520 | 650 | EXEC SQL END DECLARE SECTION; |
651 | int status; | |
652 | ||
b9f9ab1c | 653 | if (q->type == MR_Q_RETRIEVE) |
5eaef520 | 654 | return MR_SUCCESS; |
655 | ||
656 | id = *(int *)argv[1]; | |
657 | ||
b9f9ab1c | 658 | if (q->type == MR_Q_APPEND && isdigit(argv[0][0])) |
9e252e6f | 659 | return MR_BAD_CHAR; |
660 | ||
5eaef520 | 661 | EXEC SQL SELECT count(name) INTO :cnt from hostalias WHERE mach_id = :id; |
662 | if (dbms_errno) | |
663 | return mr_errcode; | |
b9f9ab1c | 664 | /* if the type is MR_Q_APPEND, this is ahal and we need to make sure there |
5eaef520 | 665 | * will be no more than 2 aliases. If it's not, it must be dhal and |
666 | * any owner will do. | |
667 | */ | |
b9f9ab1c | 668 | if (q->type == MR_Q_APPEND && cnt >= 2) |
5eaef520 | 669 | return MR_PERM; |
670 | EXEC SQL SELECT m.owner_type, m.owner_id, s.owner_type, s.owner_id | |
671 | INTO :mtype, :mid, :stype, :sid FROM machine m, subnet s | |
672 | WHERE m.mach_id = :id and s.snet_id = m.snet_id; | |
673 | status = find_member(mtype, mid, cl); | |
674 | if (status) | |
675 | return MR_SUCCESS; | |
676 | status = find_member(stype, sid, cl); | |
677 | if (status) | |
678 | return MR_SUCCESS; | |
679 | else | |
680 | return MR_PERM; | |
1beb5e23 | 681 | } |
7cf83f0f | 682 | |
683 | ||
7cf83f0f | 684 | /* access_snt - check for retrieving network structure |
685 | */ | |
686 | ||
5eaef520 | 687 | int access_snt(struct query *q, char *argv[], client *cl) |
7cf83f0f | 688 | { |
b9f9ab1c | 689 | if (q->type == MR_Q_RETRIEVE) |
5eaef520 | 690 | return MR_SUCCESS; |
7cf83f0f | 691 | |
5eaef520 | 692 | return MR_PERM; |
7cf83f0f | 693 | } |
1a9a0a59 | 694 | |
695 | ||
696 | /* access_printer */ | |
697 | int access_printer(struct query *q, char *argv[], client *cl) | |
698 | { | |
699 | EXEC SQL BEGIN DECLARE SECTION; | |
700 | char type[PRINTSERVERS_OWNER_TYPE_SIZE]; | |
701 | int id, mach_id; | |
702 | EXEC SQL END DECLARE SECTION; | |
703 | int status; | |
704 | ||
705 | mach_id = *(int *)argv[PRN_RM]; | |
706 | EXEC SQL SELECT owner_type, owner_id INTO :type, :id | |
707 | FROM printservers WHERE mach_id = :mach_id; | |
708 | if (sqlca.sqlcode) | |
709 | return MR_PERM; | |
710 | ||
711 | status = find_member(type, id, cl); | |
712 | if (status) | |
713 | return MR_SUCCESS; | |
714 | else | |
715 | return MR_PERM; | |
716 | } | |
d7ddc011 | 717 | |
718 | /* access_zephyr */ | |
719 | int access_zephyr(struct query *q, char *argv[], client *cl) | |
720 | { | |
721 | EXEC SQL BEGIN DECLARE SECTION; | |
722 | char type[ZEPHYR_OWNER_TYPE_SIZE]; | |
723 | char *class; | |
724 | int id; | |
725 | EXEC SQL END DECLARE SECTION; | |
726 | int status; | |
727 | ||
728 | class = argv[ZA_CLASS]; | |
729 | EXEC SQL SELECT owner_type, owner_id INTO :type, :id | |
730 | FROM zephyr WHERE class = :class; | |
731 | if (sqlca.sqlcode) | |
732 | return MR_PERM; | |
733 | ||
734 | status = find_member(type, id, cl); | |
735 | if (status) | |
736 | return MR_SUCCESS; | |
737 | else | |
738 | return MR_PERM; | |
739 | } | |
740 | ||
2fb668b0 | 741 | /* access_container - check access for most container operations |
742 | * | |
743 | * Inputs: argv[0] - cnt_id | |
744 | * q - query name | |
745 | * cl - client name | |
746 | * | |
747 | * - check if that client is a member of the access control list | |
748 | * - OR, if the query is add_machine_to_container or delete_machine_from_container | |
749 | * check if the client is a memeber of the mem_acl list | |
750 | * - if the query is update_container and the container is to be renamed and | |
751 | * it is a top-level container, only priviledged users can do it | |
752 | */ | |
753 | ||
754 | int access_container(struct query *q, char *argv[], client *cl) | |
755 | { | |
756 | EXEC SQL BEGIN DECLARE SECTION; | |
73155abd | 757 | int cnt_id, acl_id, memacl_id, mach_id, machine_owner_id, flag; |
2fb668b0 | 758 | char acl_type[CONTAINERS_ACL_TYPE_SIZE], memacl_type[CONTAINERS_ACL_TYPE_SIZE]; |
759 | char name[CONTAINERS_NAME_SIZE], *newname; | |
73155abd | 760 | char machine_owner_type[MACHINE_OWNER_TYPE_SIZE]; |
2fb668b0 | 761 | EXEC SQL END DECLARE SECTION; |
762 | int status; | |
763 | ||
764 | cnt_id = *(int *)argv[0]; | |
765 | ||
766 | /* if amcn or dmcn, container id is the second argument */ | |
767 | if (strcmp(q->shortname, "amcn") == 0 || strcmp(q->shortname, "dmcn") == 0) | |
73155abd | 768 | { |
769 | mach_id = *(int *)argv[0]; | |
2fb668b0 | 770 | cnt_id = *(int *)argv[1]; |
73155abd | 771 | } |
2fb668b0 | 772 | |
73155abd | 773 | EXEC SQL SELECT acl_id, acl_type, memacl_id, memacl_type, name, publicflg |
774 | INTO :acl_id, :acl_type, :memacl_id, :memacl_type, :name, :flag | |
2fb668b0 | 775 | FROM containers |
776 | WHERE cnt_id = :cnt_id; | |
777 | ||
778 | if (sqlca.sqlerrd[2] != 1) | |
779 | return MR_INTERNAL; | |
780 | ||
781 | /* trim off the trailing spaces */ | |
782 | strcpy(name, strtrim(name)); | |
783 | ||
57d62784 | 784 | /* Only dbadmin can rename containers. */ |
2fb668b0 | 785 | if (!strcmp(q->shortname, "ucon")) |
786 | { | |
787 | newname = argv[1]; | |
57d62784 | 788 | if (strcmp(name, newname)) |
2fb668b0 | 789 | return MR_PERM; |
790 | } | |
791 | ||
792 | /* check for client in access control list and return success right | |
793 | * away if it's there. */ | |
794 | if (find_member(acl_type, acl_id, cl)) | |
795 | return MR_SUCCESS; | |
796 | ||
797 | /* If not amcn, dmcn, we lose. */ | |
798 | if (strcmp(q->shortname, "amcn") && strcmp(q->shortname, "dmcn")) | |
799 | return MR_PERM; | |
800 | ||
801 | if (find_member(memacl_type, memacl_id, cl)) | |
802 | return MR_SUCCESS; | |
803 | ||
73155abd | 804 | /* if the container is public or the query is delete, grant access if client |
805 | * is on owner list */ | |
b9f9ab1c | 806 | if (flag || q->type == MR_Q_DELETE) |
73155abd | 807 | { |
808 | EXEC SQL SELECT owner_type, owner_id INTO :machine_owner_type, | |
809 | :machine_owner_id | |
810 | FROM machine | |
811 | WHERE mach_id = :mach_id; | |
812 | ||
813 | if (sqlca.sqlerrd[2] == 1 && strcmp("NONE", machine_owner_type) && | |
814 | find_member(machine_owner_type, machine_owner_id, cl)) | |
815 | return MR_SUCCESS; | |
816 | } | |
2fb668b0 | 817 | /* Otherwise fail. */ |
818 | return MR_PERM; | |
819 | } | |
7b12515f | 820 | |
821 | int check_mail_string(char *mailstring) | |
822 | { | |
823 | EXEC SQL BEGIN DECLARE SECTION; | |
824 | char mname[MACHINE_NAME_SIZE]; | |
825 | EXEC SQL END DECLARE SECTION; | |
826 | char *p, *host, *hostdomain; | |
827 | struct hostent *hp; | |
b4b129dd | 828 | struct mxentry *mxrecords = NULL; |
7b12515f | 829 | int index; |
830 | ||
831 | p = strchr(mailstring, '@'); | |
832 | if (p) | |
833 | { | |
834 | host = strdup(++p); | |
835 | ||
836 | /* Replace .LOCAL at end of host with .MIT.EDU if needed. */ | |
837 | hostdomain = strrchr(host, '.'); | |
838 | if (hostdomain && !strcasecmp(hostdomain, ".LOCAL")) | |
839 | { | |
840 | index = hostdomain - host; | |
841 | host[index] = '\0'; | |
842 | host = realloc(host, strlen(host) + strlen(".MIT.EDU") + 1); | |
843 | strcat(host, ".MIT.EDU"); | |
844 | } | |
845 | ||
846 | hp = gethostbyname(host); | |
847 | if (hp) | |
848 | { | |
849 | host = realloc(host, strlen(hp->h_name) + 1); | |
850 | if (host) | |
851 | strcpy(host, hp->h_name); | |
852 | } | |
853 | else | |
854 | { | |
b4b129dd | 855 | /* Possibly a host with no A record but MX records. Check. */ |
856 | mxrecords = getmxrecords(host); | |
857 | if (!mxrecords) | |
858 | return MR_BAD_MAIL_STRING; | |
859 | else | |
860 | return MR_SUCCESS; | |
7b12515f | 861 | } |
862 | ||
863 | if (!strcasecmp(host, "MIT.EDU")) | |
864 | { | |
865 | free(host); | |
866 | return MR_BAD_MAIL_STRING; | |
867 | } | |
868 | ||
869 | EXEC SQL DECLARE csr_listmem CURSOR FOR | |
870 | SELECT UNIQUE m.name FROM machine m, serverhosts sh | |
871 | WHERE m.mach_id = sh.mach_id | |
872 | AND (sh.service = 'MAILHUB' or sh.service = 'POSTOFFICE'); | |
873 | if (dbms_errno) | |
874 | { | |
875 | free(host); | |
876 | return mr_errcode; | |
877 | } | |
878 | EXEC SQL OPEN csr_listmem; | |
879 | if (dbms_errno) | |
880 | { | |
881 | free(host); | |
882 | return mr_errcode; | |
883 | } | |
884 | while (1) | |
885 | { | |
886 | EXEC SQL FETCH csr_listmem INTO :mname; | |
887 | if (sqlca.sqlcode) | |
888 | break; | |
889 | ||
890 | if (!strcasecmp(host, strtrim(mname))) | |
891 | { | |
892 | free(host); | |
893 | return MR_BAD_MAIL_STRING; | |
894 | } | |
895 | } | |
896 | free(host); | |
897 | } | |
898 | ||
899 | return MR_SUCCESS; | |
900 | } | |
b4b129dd | 901 | |
902 | /* | |
903 | * This ought to be in the bind library. It's adapted from sendmail. | |
904 | */ | |
905 | ||
906 | /* | |
907 | * These are defined in RFC833. Some bind interface headers don't declare them. | |
908 | * Ghod help us if they're ever actually incompatible with what's in | |
909 | * the arpa/nameser.h header. | |
910 | */ | |
911 | #ifndef PACKETSZ | |
912 | #define PACKETSZ 512 /* maximum packet size */ | |
913 | #endif | |
914 | #ifndef HFIXEDSZ | |
915 | #define HFIXEDSZ 12 /* #/bytes of fixed data in header */ | |
916 | #endif | |
917 | #ifndef INT32SZ | |
918 | #define INT32SZ 4 /* for systems without 32-bit ints */ | |
919 | #endif | |
920 | #ifndef INT16SZ | |
921 | #define INT16SZ 2 /* for systems without 16-bit ints */ | |
922 | #endif | |
923 | ||
924 | /* minimum possible size of MX record in packet */ | |
925 | #define MIN_MX_SIZE 8 /* corresp to "a.com 0" w/ terminating space */ | |
926 | ||
927 | struct mxentry *getmxrecords(const char *name) | |
928 | { | |
929 | char answer[PACKETSZ], *eom, *cp, *bp; | |
930 | int n, ancount, qdcount, buflen, type, pref, ind; | |
931 | static struct mxentry pmx[(PACKETSZ - HFIXEDSZ) / MIN_MX_SIZE]; | |
932 | static char MXHostBuf[PACKETSZ - HFIXEDSZ]; | |
933 | HEADER *hp; | |
934 | ||
935 | pmx->name = (char *)NULL; | |
936 | pmx->pref = -1; | |
937 | n = res_search(name, C_IN,T_MX, (unsigned char *)&answer, sizeof(answer)); | |
938 | if (n == -1) | |
939 | return((struct mxentry *)NULL); | |
940 | if ((size_t)n > sizeof(answer)) | |
941 | n = sizeof(answer); | |
942 | ||
943 | hp = (HEADER *)&answer; | |
944 | cp = answer + HFIXEDSZ; | |
945 | eom = answer + n; | |
946 | h_errno = 0; | |
947 | for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) | |
948 | if ((n = dn_skipname((unsigned char *)cp, (unsigned char *)eom)) < 0) | |
949 | return((struct mxentry *)NULL); | |
950 | buflen = sizeof(MXHostBuf) - 1; | |
951 | bp = MXHostBuf; | |
952 | ind = 0; | |
953 | ancount = ntohs(hp->ancount); | |
954 | while (--ancount >= 0 && cp < eom) | |
955 | { | |
956 | if ((n = dn_expand((unsigned char *)answer, (unsigned char *)eom, | |
957 | (unsigned char *)cp, bp, buflen)) < 0) | |
958 | break; | |
959 | cp += n; | |
960 | GETSHORT(type, cp); | |
961 | cp += INT16SZ + INT32SZ; | |
962 | GETSHORT(n, cp); | |
963 | if (type != T_MX) | |
964 | { | |
965 | cp += n; | |
966 | continue; | |
967 | } | |
968 | GETSHORT(pref, cp); | |
969 | if ((n = dn_expand((unsigned char *)answer, (unsigned char *)eom, | |
970 | (unsigned char *)cp, bp, buflen)) < 0) | |
971 | break; | |
972 | cp += n; | |
973 | ||
974 | pmx[ind].name = bp; | |
975 | pmx[ind].pref = pref; | |
976 | ++ind; | |
977 | ||
978 | n = strlen((const char *)bp); | |
979 | bp += n; | |
980 | *bp++ = '\0'; | |
981 | ||
982 | buflen -= n + 1; | |
983 | } | |
984 | ||
985 | pmx[ind].name = (char *)NULL; | |
986 | pmx[ind].pref = -1; | |
987 | return(pmx); | |
988 | } |