/* $Id$ * * This generates acl files for various servers. * * Copyright (C) 1999 by the Massachusetts Institute of Technology. * For copying and distribution information, please see the file * . */ #include #include #include #include "util.h" #include #include #include #include #include #ifdef HAVE_KRB4 #include #endif EXEC SQL INCLUDE sqlca; RCSID("$Header$"); char *whoami = "acl.gen"; char *db = "moira/moira"; void dump_access_file(FILE *out, int lid); char *merge_access_bits(char *t1, char *t2); void dump_discuss_acl(FILE *out, int lid); void dump_passwd_file(FILE *out, int lid); void dump_group_file(FILE *out, int id); char *merge_discuss_acls(char *one, char *two); void sqlerr(void); int main(int argc, char **argv) { EXEC SQL BEGIN DECLARE SECTION; int mid, lid, discuss_uid = -1; char target[ACL_TARGET_SIZE], kind[ACL_KIND_SIZE]; char host[MACHINE_NAME_SIZE]; EXEC SQL END DECLARE SECTION; char filename[MAXPATHLEN]; TARFILE *tf; FILE *out; struct save_queue *sq; time_t now = time(NULL); EXEC SQL WHENEVER SQLERROR DO sqlerr(); EXEC SQL CONNECT :db; init_acls(); EXEC SQL DECLARE csr_mach CURSOR FOR SELECT UNIQUE mach_id FROM acl; EXEC SQL OPEN csr_mach; while (1) { EXEC SQL FETCH csr_mach INTO :mid; if (sqlca.sqlcode) break; EXEC SQL SELECT name INTO :host FROM machine WHERE mach_id = :mid; if (sqlca.sqlcode) continue; strtrim(host); sprintf(filename, "%s/acl/%s", DCM_DIR, host); tf = tarfile_open(filename); EXEC SQL DECLARE csr_acl CURSOR FOR SELECT target, kind, list_id FROM acl WHERE mach_id = :mid; EXEC SQL OPEN csr_acl; while (1) { EXEC SQL FETCH csr_acl INTO :target, :kind, :lid; if (sqlca.sqlcode) break; strtrim(target); strtrim(kind); if (!strcasecmp(kind, "discuss")) { /* Discuss acls need to be owned by discuss. */ if (discuss_uid == -1) { EXEC SQL SELECT unix_uid INTO :discuss_uid FROM users WHERE login = 'discuss'; } out = tarfile_start(tf, target, 0644, discuss_uid, 1, "discuss", "daemon", now); dump_discuss_acl(out, lid); } else { /* Otherwise own by root? Perhaps the acl table should * say, really... */ out = tarfile_start(tf, target, 0644, 0, 0, "root", "root", now); if (!strcasecmp(kind, "kerberos4")) dump_krb_acl(out, "LIST", lid, 4); else if (!strcasecmp(kind, "kerberos5")) dump_krb_acl(out, "LIST", lid, 5); else if (!strcasecmp(kind, "access")) dump_access_file(out, lid); else if (!strcasecmp(kind, "passwd")) dump_passwd_file(out, lid); else if (!strcasecmp(kind, "group")) dump_group_file(out, lid); else if (!strcasecmp(kind, "userlist")) dump_user_list(out, "LIST", lid); } tarfile_end(tf); } tarfile_close(tf); } EXEC SQL COMMIT RELEASE; exit(MR_SUCCESS); } void dump_access_file(FILE *out, int lid) { struct save_queue *sq = get_acl("LIST", lid, merge_access_bits); struct imember *m; char *name, *lasts = NULL; int i = 0; while (sq_remove_data(sq, &m)) { if (m->type == 'U' || (m->type == 'S' && m->name[0] == '*')) { if (*(m->tag)) fprintf(out, "%-10s %s\n", m->name, m->tag); else fprintf(out, "%-10s rl\n", m->name); } else if (m->type == 'K') { name = strtok_r(m->name, "@", &lasts); EXEC SQL SELECT count(login) INTO :i FROM users WHERE login = :name and status != 3; if (i == 1) { if (*(m->tag)) fprintf(out, "%-10s %s\n", m->name, m->tag); else fprintf(out, "%-10s rl\n", m->name); } } freeimember(m); } sq_destroy(sq); } char *merge_access_bits(char *t1, char *t2) { char *m = NULL, *ans; int r = 1, l = 1; /* Clear bits that aren't granted by both tags. */ if ((*t1 && t1[0] != 'r' && t1[1] != 'r') || (*t2 && t2[0] != 'r' && t2[1] != 'r')) r = 0; if ((*t1 && t1[0] != 'l' && t1[1] != 'l') || (*t2 && t2[0] != 'l' && t2[1] != 'l')) l = 0; if (!r || !l) { /* Skip to message part of tag 1 */ m = t1; while (*m && !isspace(*m)) m++; while (isspace(*m)) m++; /* If nothing there, skip to message part of tag 2 */ if (!*m) { m = t2; while (*m && !isspace(*m)) m++; while (isspace(*m)) m++; } ans = malloc(4 + strlen(m)); sprintf(ans, "%c %s", r ? 'r' : l ? 'l' : '-', m); } else { ans = malloc(3); strcpy(ans, "rl"); } return ans; } void dump_discuss_acl(FILE *out, int lid) { struct save_queue *sq = get_acl("LIST", lid, merge_discuss_acls); struct imember *m; char name[STRINGS_STRING_SIZE], *bits; char starbits[8] = { 0 }; int num; num = 0; while (sq_get_data(sq, &m)) { if (m->type != 'S' || !strcmp(m->name, "*")) num++; } fprintf(out, "%d\n", num); while (sq_remove_data(sq, &m)) { bits = merge_discuss_acls(m->tag, ""); if (m->type != 'S') { canon_krb(m, 4, name, sizeof(name)); fprintf(out, "%s:%s\n", bits, name); } else if (!strcmp(m->name, "*")) strcpy(starbits, bits); free(bits); freeimember(m); } sq_destroy(sq); /* Discuss ACLs are ordered, so "*" must come last. */ if (*starbits) fprintf(out, "%s:*\n", starbits); } char *merge_discuss_acls(char *one, char *two) { char bits[8]; sprintf(bits, "%c%c%c%c%c%c%c", strchr(one, 'a') || strchr(two, 'a') ? 'a' : ' ', strchr(one, 'c') || strchr(two, 'c') ? 'c' : ' ', strchr(one, 'd') || strchr(two, 'd') ? 'd' : ' ', strchr(one, 'o') || strchr(two, 'o') ? 'o' : ' ', strchr(one, 'r') || strchr(two, 'r') ? 'r' : ' ', strchr(one, 's') || strchr(two, 's') ? 's' : ' ', strchr(one, 'w') || strchr(two, 'w') ? 'w' : ' '); return strdup(bits); } void dump_passwd_file(FILE *out, int lid) { struct save_queue *sq = get_acl("LIST", lid, NULL); struct imember *m; EXEC SQL BEGIN DECLARE SECTION; char shell[USERS_SHELL_SIZE], fullname[USERS_FULLNAME_SIZE]; char nickname[USERS_NICKNAME_SIZE], oa[USERS_OFFICE_ADDR_SIZE]; char op[USERS_OFFICE_PHONE_SIZE], hp[USERS_HOME_PHONE_SIZE]; int uid, i = 0; char *name, *n, *lasts = NULL; EXEC SQL END DECLARE SECTION; while (sq_remove_data(sq, &m)) { switch (m->type) { case 'U': name = m->name; EXEC SQL SELECT unix_uid, shell, fullname, nickname, office_addr, office_phone, home_phone INTO :uid, :shell, :fullname, :nickname, :oa, :op, :hp FROM users WHERE login = :name AND status != 3; if (sqlca.sqlcode) continue; strtrim(shell); strtrim(fullname); strtrim(nickname); strtrim(op); strtrim(oa); strtrim(hp); fprintf(out, "%s:*:%d:101:%s,%s,%s,%s,%s:/mit/%s:%s\n", name, uid, fullname, nickname, oa, op, hp, name, shell); break; case 'K': name = strtok_r(m->name, "@", &lasts); EXEC SQL SELECT count(login) INTO :i FROM users WHERE login = :name and status != 3; if (i == 1) { EXEC SQL SELECT unix_uid, shell, fullname, nickname, office_addr, office_phone, home_phone INTO :uid, :shell, :fullname, :nickname, :oa, :op, :hp FROM users WHERE login = :name AND status != 3; if (sqlca.sqlcode) continue; strtrim(shell); strtrim(fullname); strtrim(nickname); strtrim(op); strtrim(oa); strtrim(hp); fprintf(out, "%s:*:%d:101:%s,%s,%s,%s,%s:/mit/%s:%s\n", name, uid, fullname, nickname, oa, op, hp, name, shell); } break; } freeimember(m); } sq_destroy(sq); } /* This one is a bit weird since we actually look at top-level * lists rather then flattening them. */ void dump_group_file(FILE *out, int id) { EXEC SQL BEGIN DECLARE SECTION; int lid = id, mid, gid, grouplist, i = 0; char mtype[IMEMBERS_MEMBER_TYPE_SIZE], name[LIST_NAME_SIZE]; EXEC SQL END DECLARE SECTION; struct save_queue *sq; struct imember *m; char *maybecomma, *s, *n, *lasts = NULL; EXEC SQL DECLARE csr_grp CURSOR FOR SELECT member_type, member_id FROM imembers WHERE list_id = :lid AND direct = 1; EXEC SQL OPEN csr_grp; while (1) { EXEC SQL FETCH csr_grp INTO :mtype, :mid; if (sqlca.sqlcode) break; switch (*mtype) { case 'L': EXEC SQL SELECT name, gid, grouplist INTO :name, :gid, :grouplist FROM list WHERE list_id = :mid; if (sqlca.sqlcode || !grouplist) break; strtrim(name); fprintf(out, "%s:*:%d:", name, gid); sq = get_acl("LIST", mid, NULL); maybecomma = ""; while (sq_remove_data(sq, &m)) { if (m->type == 'U') { fprintf(out, "%s%s", maybecomma, m->name); maybecomma = ","; } else if (m->type == 'K') { n = strtok_r(m->name, "@", &lasts); EXEC SQL SELECT count(login) INTO :i FROM users WHERE login = :n and status != 3; if (i == 1) { fprintf(out, "%s%s", maybecomma, n); maybecomma = ","; } } freeimember(m); } fprintf(out, "\n"); sq_destroy(sq); break; } } EXEC SQL CLOSE csr_grp; } void sqlerr(void) { db_error(sqlca.sqlcode); exit(MR_DBMS_ERR); }