/* $Id: qsetup.pc 4063 2012-01-04 17:37:23Z zacheiss $ * * Query setup routines * * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology * For copying and distribution information, please see the file * . */ #include #include "mr_server.h" #include "query.h" #include "qrtn.h" #include #include #include #include #include EXEC SQL INCLUDE sqlca; RCSID("$HeadURL: svn+ssh://svn.mit.edu/moira/trunk/moira/server/qsetup.pc $ $Id: qsetup.pc 4063 2012-01-04 17:37:23Z zacheiss $"); extern char *whoami; extern int dbms_errno, mr_errcode; EXEC SQL BEGIN DECLARE SECTION; extern char stmt_buf[]; EXEC SQL END DECLARE SECTION; EXEC SQL WHENEVER SQLERROR DO dbmserr(); int hostname_check(char *name); int hostinfo_check(char *name, int num); int prefetch_value(struct query *q, char **argv, client *cl); int check_nfs(int mach_idx, char *name, char *access); int check_mailman_listname(char *name, const char *suffix); /* Setup Routines */ /* Setup routine for add_user * * Inputs: argv[0] - login * argv[1] - uid * * Description: * * - if argv[1] == UNIQUE_UID then set argv[1] = next(uid) * - if argv[0] == UNIQUE_LOGIN then set argv[0] = "#" */ int setup_ausr(struct query *q, char *argv[], client *cl) { int row, err; EXEC SQL BEGIN DECLARE SECTION; int nuid; EXEC SQL END DECLARE SECTION; if (!strcmp(q->shortname, "uusr") || !strcmp(q->shortname, "uuac")) row = 2; else row = 1; if (q->version > 2) { if (strlen(argv[row + 3]) + strlen(argv[row + 4]) + strlen(argv[row + 5]) + 2 > USERS_FULLNAME_SIZE) return MR_ARG_TOO_LONG; } else { if (strlen(argv[row + 2]) + strlen(argv[row + 3]) + strlen(argv[row + 4]) + 2 > USERS_FULLNAME_SIZE) return MR_ARG_TOO_LONG; } if (q->version > 10) { /* For both winhomedir and winprofiledir, we allow values of the * following forms: * * [AFS] - Magic token for AFS home directory. * [LOCAL] - Magic token for AD default local values, i.e. C:\ * [DFS] - Magic token for DFS home directory * UNC pathname - \\ * local pathname - : */ if ((strcasecmp(argv[row + 12], "[AFS]")) && (strcasecmp(argv[row + 12], "[LOCAL]")) && (strcasecmp(argv[row + 12], "[DFS]")) && (!(argv[row + 12][0] == '\\' && argv[row + 12][1] == '\\')) && (!(isalpha(*argv[row + 12]) && (argv[row + 12][1] == ':')))) return MR_BAD_CHAR; if ((strcasecmp(argv[row + 13], "[AFS]")) && (strcasecmp(argv[row + 13], "[LOCAL]")) && (strcasecmp(argv[row + 13], "[DFS]")) && (!(argv[row + 13][0] == '\\' && argv[row + 13][1] == '\\')) && (!(isalpha(*argv[row + 13]) && (argv[row + 13][1] == ':')))) return MR_BAD_CHAR; } if (!strcmp(argv[row], UNIQUE_UID) || atoi(argv[row]) == -1) { if ((err = set_next_object_id("unix_uid", USERS_TABLE, 1))) return err; EXEC SQL SELECT value INTO :nuid FROM numvalues WHERE name = 'unix_uid'; if (sqlca.sqlerrd[2] != 1) return MR_INTERNAL; sprintf(argv[row], "%d", nuid); } if (!strcmp(argv[0], UNIQUE_LOGIN) || atoi(argv[row]) == -1) sprintf(argv[0], "#%s", argv[row]); if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS) return mr_errcode; /* If this is an MR_Q_UPDATE query, we're done. */ if (row == 2) return MR_SUCCESS; /* For an add query, we need to fill in the creator id. */ sprintf(argv[q->argc + q->vcnt + 1], "%d", cl->client_id); return MR_SUCCESS; } /* setup_dusr - verify that the user is no longer being referenced * and may safely be deleted. */ int setup_dusr(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int flag, id, cnt; char resv[USERS_RESERVATIONS_SIZE]; EXEC SQL END DECLARE SECTION; id = *(int *)argv[0]; /* For now, only allow users to be deleted if their status is * one of 0, 4, or 8 (the various registerable statuses) * and we have no reservations about deleting them. */ EXEC SQL SELECT status, reservations INTO :flag, :resv FROM users WHERE users_id = :id; if ((flag != 0 && flag != 4 && flag != 8) || *resv) return MR_IN_USE; EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers WHERE member_id = :id AND member_type = 'USER'; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(label) INTO :cnt FROM filesys WHERE owner = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(name) INTO :cnt FROM list WHERE acl_id = :id AND acl_type = 'USER'; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(name) INTO :cnt FROM servers WHERE acl_id = :id AND acl_type = 'USER'; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(acl_id) INTO :cnt FROM hostaccess WHERE acl_id = :id AND acl_type = 'USER'; if (cnt > 0) return MR_IN_USE; if (dbms_errno) return mr_errcode; EXEC SQL DELETE FROM quota WHERE entity_id = :id AND type = 'USER'; EXEC SQL DELETE FROM krbmap WHERE users_id = :id; return MR_SUCCESS; } /* setup_dpob: Take care of keeping track of the post office usage. */ int setup_dpob(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int id, user; char type[USERS_POTYPE_SIZE]; EXEC SQL END DECLARE SECTION; user = *(int *)argv[0]; EXEC SQL SELECT potype, pop_id INTO :type, :id FROM users WHERE users_id = :user; if (dbms_errno) return mr_errcode; if (!strcmp(strtrim(type), "POP")) set_pop_usage(id, -1); return MR_SUCCESS; } /* setup_dmac - verify that the machine is no longer being referenced * and may safely be deleted. */ int setup_dmac(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int flag, id, cnt; EXEC SQL END DECLARE SECTION; id = *(int *)argv[0]; EXEC SQL SELECT status INTO :flag FROM machine WHERE mach_id = :id; if (flag != 3) return MR_IN_USE; EXEC SQL SELECT COUNT(login) INTO :cnt FROM users WHERE potype = 'POP' AND pop_id = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(login) INTO :cnt FROM users WHERE potype = 'EXCHANGE' and exchange_id = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM serverhosts WHERE mach_id = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM nfsphys WHERE mach_id = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM hostaccess WHERE mach_id = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM printers WHERE mach_id = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(rm) INTO :cnt FROM printers WHERE rm = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(rq) INTO :cnt FROM printers WHERE rq = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM printservers WHERE mach_id = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM hostalias WHERE mach_id = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers WHERE member_type = 'MACHINE' and member_id = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL DELETE FROM mcmap WHERE mach_id = :id; if (dbms_errno) return mr_errcode; EXEC SQL DELETE FROM mcntmap WHERE mach_id = :id; if (dbms_errno) return mr_errcode; return MR_SUCCESS; } /* setup_asnt - verify that the data entered for the subnet is sane. * In particular, make sure that the "low" and "high" addresses are * correctly ordered, i.e., high > low. */ int setup_asnt(struct query *q, char *argv[], client *cl) { int high, low, row, status; char *account_number; /* Check for asnt or usnt. */ if (q->type == MR_Q_APPEND) row = 0; else row = 1; low = atoi(argv[row + 7]); high = atoi(argv[row + 8]); status = atoi(argv[row + 2]); account_number = argv[row + 4]; /* Don't allow Private subnets to be created without a valid billing * number. */ if (status == SNET_STATUS_PRIVATE_10MBPS || status == SNET_STATUS_PRIVATE_100MBPS || status == SNET_STATUS_PRIVATE_1000MBPS) { EXEC SQL SELECT account_number FROM accountnumbers WHERE account_number = :account_number; if (sqlca.sqlcode == SQL_NO_MATCH) return MR_ACCOUNT_NUMBER; } /* Special case 0.0.0.0 and 255.255.255.255 */ if (!(low == 0 || low == -1 || high == 0 || high == -1)) if (low > high) return MR_ADDRESS; /* If this is update_subnet, we're done. */ if (row == 1) return MR_SUCCESS; /* For an add_subnet query, allocate and fill in a new snet_id */ if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS) return mr_errcode; return MR_SUCCESS; } /* setup_dsnt - verify that the subnet is no longer being referenced * and may safely be deleted. */ int setup_dsnt(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int id, cnt = 0; EXEC SQL END DECLARE SECTION; id = *(int *)argv[0]; EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM machine WHERE snet_id = :id; if (cnt > 0) return MR_IN_USE; return MR_SUCCESS; } /* setup_dclu - verify that the cluster is no longer being referenced * and may safely be deleted. */ int setup_dclu(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int id, cnt; EXEC SQL END DECLARE SECTION; id = *(int *)argv[0]; EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM mcmap WHERE clu_id = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(clu_id) INTO :cnt FROM svc WHERE clu_id = :id; if (cnt > 0) return MR_IN_USE; if (dbms_errno) return mr_errcode; return MR_SUCCESS; } /* setup_alis - if argv[5] is non-zero and argv[6] is UNIQUE_ID, then allocate * a new gid and put it in argv[6]. Otherwise if argv[6] is UNIQUE_ID but * argv[5] is not, then remember that UNIQUE_ID is being stored by putting * a -1 there. Remember that this is also used for ulis, with the indexes * at 6 & 7. Also check that the list name does not contain uppercase * characters, control characters, @, or :. * * Newlines in list descriptions do bad things to the aliases file * moira generates, so make sure the description doesn't contain any, too. */ static int badlistchars[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */ 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, /* SPACE - / */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 0 - ? */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* @ - O */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* P - _ */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* p - ^? */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; static const char *mailman_suffixes[] = { "-admin", "-owner", "-request", "-bounces", "-confirm", "-join", "-leave", "-subscribe", "-unsubscribe", NULL }; int setup_alis(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int ngid, cnt, mailman, mailman_id, lid; char *name, *desc; EXEC SQL END DECLARE SECTION; unsigned char *p; int idx, err, best = -1, usage, i; if (!strcmp(q->shortname, "alis")) idx = 0; else if (!strcmp(q->shortname, "ulis")) idx = 1; name = argv[idx]; if (q->version == 2) desc = argv[9 + idx]; else if (q->version == 3) desc = argv[10 + idx]; else if (q->version == 4) desc = argv[12 + idx]; else if (q->version >= 10) desc = argv[14 + idx]; if (idx == 1) { lid = *(int *)argv[0]; if (acl_access_check(lid, cl)) return MR_PERM; } for (p = (unsigned char *) name; *p; p++) { if (badlistchars[*p]) return MR_BAD_CHAR; } for (p = (unsigned char *) desc; *p; p++) { if (*p == '\n') return MR_BAD_CHAR; } /* Check that it doesn't conflict with a pre-existing weirdly-cased * name. */ EXEC SQL SELECT COUNT(name) INTO :cnt FROM list WHERE LOWER(name) = :name AND name != :name; if (cnt) return MR_EXISTS; if (!strcmp(argv[6 + idx], UNIQUE_GID) || atoi(argv[6 + idx]) == -1) { if (atoi(argv[5 + idx])) { if ((err = set_next_object_id("gid", LIST_TABLE, 1))) return err; EXEC SQL SELECT value INTO :ngid FROM numvalues WHERE name = 'gid'; if (dbms_errno) return mr_errcode; sprintf(argv[6 + idx], "%d", ngid); } else strcpy(argv[6 + idx], "-1"); } /* Don't let someone rename a list to one of the magic mailman names * (foo-admin, etc) if foo already exists as a mailman list. */ for (i = 0; mailman_suffixes[i]; i++) { if ((err = check_mailman_listname(name, mailman_suffixes[i])) != MR_SUCCESS) return err; } if (q->version >= 10) { /* Don't let them take this name for a mailman list if we can't * reserve the -admin, -owner, and -request names. */ if (atoi(argv[8 + idx])) { EXEC SQL SELECT COUNT(name) INTO :cnt FROM list WHERE (name = :name || '-admin' OR name = :name || '-owner' OR name = :name || '-request'); if (cnt) return MR_EXISTS; } /* Handle the [ANY] case for mailman server. */ mailman_id = *(int *)argv[9 + idx]; if (mailman_id == -1) { EXEC SQL DECLARE csr_mailman CURSOR FOR SELECT mach_id FROM serverhosts WHERE service = 'MAILMAN' AND enable = 1; if (dbms_errno) return mr_errcode; EXEC SQL OPEN csr_mailman; if (dbms_errno) return mr_errcode; while (1) { EXEC SQL FETCH csr_mailman INTO :mailman_id; if (sqlca.sqlcode) break; EXEC SQL SELECT COUNT(name) INTO :usage FROM list WHERE mailman_id = :mailman_id; if (best < 0 || usage < best) { best = usage; *(int *)argv[9 + idx] = mailman_id; break; } } EXEC SQL CLOSE csr_mailman; if (dbms_errno) return mr_errcode; if (best == -1) return MR_SERVICE; } } else { /* Client too old to know about the mailman code. * Use existing value of mailman boolean in the table. * Don't do this for add_list from an old client, since the row * they're creating won't exist yet, and there's no way for them to * create a list with the mailman bit set, anyway. */ if (idx == 1) { EXEC SQL SELECT mailman INTO :mailman FROM list WHERE list_id = :lid; if (mailman) { EXEC SQL SELECT COUNT(name) INTO :cnt FROM list WHERE (name = :name || '-admin' OR name = :name || '-owner' OR name = :name || '-request'); if (cnt) return MR_EXISTS; } } } if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS) return mr_errcode; return MR_SUCCESS; } /* setup_dlis - verify that the list is no longer being referenced * and may safely be deleted. */ int setup_dlis(struct query *q, char *argv[], client *cl) { int id; EXEC SQL BEGIN DECLARE SECTION; int cnt; EXEC SQL END DECLARE SECTION; id = *(int *)argv[0]; EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers WHERE member_id = :id AND member_type = 'LIST'; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers WHERE member_id = :id AND member_type = 'LIST'; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(member_id) INTO :cnt FROM imembers WHERE list_id = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(label) INTO :cnt FROM filesys WHERE owners = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(tag) INTO :cnt FROM capacls WHERE list_id = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(name) INTO :cnt FROM list WHERE acl_id = :id AND acl_type = 'LIST' AND list_id != :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(name) INTO :cnt FROM list WHERE memacl_id = :id AND memacl_type = 'LIST' AND list_id != :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(name) INTO :cnt FROM servers WHERE acl_id = :id AND acl_type = 'LIST'; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(entity_id) INTO :cnt FROM quota WHERE entity_id = :id AND type = 'GROUP'; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(acl_id) INTO :cnt FROM hostaccess WHERE acl_id = :id AND acl_type = 'LIST'; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(class) INTO :cnt FROM zephyr z WHERE z.xmt_type = 'LIST' AND z.xmt_id = :id OR z.sub_type = 'LIST' AND z.sub_id = :id OR z.iws_type = 'LIST' AND z.iws_id = :id OR z.iui_type = 'LIST' AND z.iui_id = :id OR z.owner_type = 'LIST' and z.owner_id = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(name) INTO :cnt FROM printers WHERE lpc_acl = :id OR ac = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM printservers WHERE owner_type = 'LIST' AND owner_id = :id OR lpc_acl = :id; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT count(name) INTO :cnt FROM containers WHERE acl_id = :id AND acl_type = 'LIST'; if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT count(name) INTO :cnt FROM containers WHERE memacl_id = :id AND memacl_type = 'LIST'; if (cnt > 0) return MR_IN_USE; return MR_SUCCESS; } /* setup_dsin - verify that the service is no longer being referenced * and may safely be deleted. */ int setup_dsin(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int ec, cnt; char *svrname; EXEC SQL END DECLARE SECTION; svrname = argv[0]; EXEC SQL SELECT COUNT(service) INTO :cnt FROM serverhosts WHERE service = UPPER(:svrname); if (cnt > 0) return MR_IN_USE; EXEC SQL SELECT inprogress INTO :ec FROM servers WHERE name = UPPER(:svrname); if (dbms_errno) return mr_errcode; if (ec) return MR_IN_USE; return MR_SUCCESS; } /* setup_dshi - verify that the service-host is no longer being referenced * and may safely be deleted. */ int setup_dshi(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int id, ec; char *svrname; EXEC SQL END DECLARE SECTION; svrname = argv[0]; id = *(int *)argv[1]; EXEC SQL SELECT inprogress INTO :ec FROM serverhosts WHERE service = UPPER(:svrname) AND mach_id = :id; if (dbms_errno) return mr_errcode; if (ec) return MR_IN_USE; return MR_SUCCESS; } /** ** setup_add_filesys - verify existance of referenced file systems ** ** Inputs: Add ** argv[1] - type ** argv[2] - mach_id ** argv[3] - name ** argv[5] - rwaccess ** ** Description: ** - for type = RVD: ** * allow anything ** - for type = NFS/IMAP: ** * extract directory prefix from name ** * verify mach_id/dir in nfsphys ** * verify rwaccess in {r, w, R, W} ** ** Side effect: sets variable _var_phys_id to the ID of the physical ** filesystem (nfsphys_id for NFS, 0 for RVD) ** ** Errors: ** MR_NFS - specified directory not exported ** MR_FILESYS_ACCESS - invalid filesys access ** **/ EXEC SQL BEGIN DECLARE SECTION; int _var_phys_id; EXEC SQL END DECLARE SECTION; int setup_afil(struct query *q, char *argv[], client *cl) { char *type, *name; int mach_id; EXEC SQL BEGIN DECLARE SECTION; int ok; char ftype[FILESYS_TYPE_SIZE + 10], *rwaccess; EXEC SQL END DECLARE SECTION; type = argv[1]; mach_id = *(int *)argv[2]; name = argv[3]; rwaccess = argv[5]; _var_phys_id = 0; sprintf(ftype, "fs_access_%s", type); EXEC SQL SELECT COUNT(trans) INTO :ok FROM alias WHERE name = :ftype AND type = 'TYPE' and trans = :rwaccess; if (dbms_errno) return mr_errcode; if (ok == 0) return MR_FILESYS_ACCESS; if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS) return mr_errcode; if (!strcmp(type, "NFS") || !strcmp(type, "IMAP")) return check_nfs(mach_id, name, rwaccess); return MR_SUCCESS; } /* Verify the arguments, depending on the FStype. Also, if this is an * NFS filesystem, then update any quotas for that filesystem to reflect * the new phys_id. */ int setup_ufil(struct query *q, char *argv[], client *cl) { int mach_id, status; char *type, *name; EXEC SQL BEGIN DECLARE SECTION; int fid, total, who, ok; char *entity, ftype[FILESYS_TYPE_SIZE + 10], *access; short int total_null; EXEC SQL END DECLARE SECTION; _var_phys_id = 0; type = argv[2]; mach_id = *(int *)argv[3]; name = argv[4]; access = argv[6]; fid = *(int *)argv[0]; who = cl->client_id; entity = cl->entity; sprintf(ftype, "fs_access_%s", type); EXEC SQL SELECT COUNT(trans) INTO :ok FROM alias WHERE name = :ftype AND type = 'TYPE' AND trans = :access; if (dbms_errno) return mr_errcode; if (ok == 0) return MR_FILESYS_ACCESS; EXEC SQL SELECT type INTO :ftype FROM filesys WHERE filsys_id = :fid; if (dbms_errno) return mr_errcode; if (!strcmp(type, "NFS") || !strcmp(type, "IMAP")) { status = check_nfs(mach_id, name, access); EXEC SQL UPDATE quota SET phys_id = :_var_phys_id WHERE filsys_id = :fid; if (dbms_errno) return mr_errcode; return status; } else if (!strcmp(type, "AFS") && strcmp(strtrim(ftype), "AFS") && strcmp(strtrim(ftype), "ERR")) { total = 0; EXEC SQL DELETE FROM quota WHERE type = 'ANY' AND filsys_id = :fid; EXEC SQL SELECT SUM (quota) INTO :total:total_null FROM quota WHERE filsys_id = :fid AND phys_id != 0; if (dbms_errno) return mr_errcode; if (!total_null && (total != 0)) { EXEC SQL INSERT INTO quota (quota, filsys_id, phys_id, entity_id, type, modtime, modby, modwith) VALUES (:total, :fid, 0, 0, 'ANY', SYSDATE, :who, :entity); if (dbms_errno) return mr_errcode; } } else { EXEC SQL UPDATE quota SET phys_id = 0 WHERE filsys_id = :fid; if (dbms_errno) return mr_errcode; } return MR_SUCCESS; } /* Find the NFS physical partition that the named directory is on. * This is done by comparing the dir against the mount point of the * partition. To make sure we get the correct match when there is * more than one, we sort the query in reverse order by dir name. */ int check_nfs(int mach_id, char *name, char *access) { EXEC SQL BEGIN DECLARE SECTION; char dir[NFSPHYS_DIR_SIZE]; int mid = mach_id; EXEC SQL END DECLARE SECTION; int status; char *cp1; char *cp2; status = MR_NFS; EXEC SQL DECLARE csr101 CURSOR FOR SELECT nfsphys_id, dir FROM nfsphys WHERE mach_id = :mid ORDER BY 2 DESC; if (dbms_errno) return mr_errcode; EXEC SQL OPEN csr101; if (dbms_errno) return mr_errcode; while (1) { EXEC SQL FETCH csr101 INTO :_var_phys_id, :dir; if (sqlca.sqlcode) break; cp1 = name; cp2 = strtrim(dir); while (*cp2) { if (*cp1++ != *cp2) break; cp2++; } if (!*cp2) { status = MR_SUCCESS; break; } } EXEC SQL CLOSE csr101; if (dbms_errno) return mr_errcode; return status; } /* setup_dfil: free any quota records and fsgroup info associated with * a filesystem when it is deleted. Also adjust the allocation numbers. */ int setup_dfil(struct query *q, char **argv, client *cl) { EXEC SQL BEGIN DECLARE SECTION; int id, total, phys_id; short int none; EXEC SQL END DECLARE SECTION; id = *(int *)argv[0]; EXEC SQL SELECT SUM (quota) INTO :total:none FROM quota WHERE filsys_id = :id; if (none) total = 0; /** What if there are multiple phys_id's per f/s? (bad data) **/ EXEC SQL SELECT phys_id INTO :phys_id FROM filesys WHERE filsys_id = :id; EXEC SQL UPDATE nfsphys SET allocated = allocated - :total WHERE nfsphys_id = :phys_id; if (!none) EXEC SQL DELETE FROM quota WHERE filsys_id = :id; EXEC SQL DELETE FROM fsgroup WHERE filsys_id = :id; EXEC SQL DELETE FROM fsgroup WHERE group_id = :id; if (dbms_errno) return mr_errcode; return MR_SUCCESS; } /* setup_dnfp: check to see that the nfs physical partition does not have * any filesystems assigned to it before allowing it to be deleted. */ int setup_dnfp(struct query *q, char **argv, client *cl) { EXEC SQL BEGIN DECLARE SECTION; int id, cnt; char *dir; EXEC SQL END DECLARE SECTION; id = *(int *)argv[0]; dir = argv[1]; EXEC SQL SELECT count(fs.rowid) INTO :cnt FROM filesys fs, nfsphys np WHERE fs.mach_id = :id AND fs.phys_id = np.nfsphys_id AND np.mach_id = :id AND np.dir = :dir; if (cnt > 0) return MR_IN_USE; if (dbms_errno) return mr_errcode; return MR_SUCCESS; } /* setup_dqot: Remove allocation from nfsphys before deleting quota. * argv[0] = filsys_id * argv[1] = type if "update_quota" or "delete_quota" * argv[2 or 1] = users_id or list_id */ int setup_dqot(struct query *q, char **argv, client *cl) { EXEC SQL BEGIN DECLARE SECTION; int quota, fs, id, physid; char *qtype; EXEC SQL END DECLARE SECTION; fs = *(int *)argv[0]; if (!strcmp(q->name, "update_quota") || !strcmp(q->name, "delete_quota")) { qtype = argv[1]; id = *(int *)argv[2]; } else { qtype = "USER"; id = *(int *)argv[1]; } EXEC SQL SELECT quota INTO :quota FROM quota WHERE type = :qtype AND entity_id = :id AND filsys_id = :fs; EXEC SQL SELECT phys_id INTO :physid FROM filesys WHERE filsys_id = :fs; EXEC SQL UPDATE nfsphys SET allocated = allocated - :quota WHERE nfsphys_id = :physid; if (dbms_errno) return mr_errcode; return MR_SUCCESS; } /* prefetch_value(): * This routine fetches an appropriate value from the numvalues table. * It is a little hack to get around the fact that SQL doesn't let you * do something like INSERT INTO table (foo) VALUES (other_table.bar). * * It is called from the query table as (*v->pre_rtn)(q, Argv, cl) or * from within a setup_...() routine with the appropriate arguments. * * Correct functioning of this routine may depend on the assumption * that this query is an MR_Q_APPEND. */ int prefetch_value(struct query *q, char **argv, client *cl) { EXEC SQL BEGIN DECLARE SECTION; char *name = q->validate->object_id; int value; EXEC SQL END DECLARE SECTION; int status, limit, argc; /* set next object id, limiting it if necessary */ if (!strcmp(name, "unix_uid") || !strcmp(name, "gid")) limit = 1; /* So far as I know, this isn't needed. Just CMA. */ else limit = 0; if ((status = set_next_object_id(name, q->rtable, limit)) != MR_SUCCESS) return status; /* fetch object id */ EXEC SQL SELECT value INTO :value FROM numvalues WHERE name = :name; if (dbms_errno) return mr_errcode; if (sqlca.sqlerrd[2] != 1) return MR_INTERNAL; argc = q->argc + q->vcnt; /* end of Argv for MR_Q_APPENDs */ sprintf(argv[argc], "%d", value); return MR_SUCCESS; } /* prefetch_filesys(): * Fetches the phys_id from filesys based on the filsys_id in argv[0]. * Appends the filsys_id and the phys_id to the argv so they can be * referenced in an INSERT into a table other than filesys. Also * see comments at prefetch_value(). * * Assumes the existence of a row where filsys_id = argv[0], since a * filesys label has already been resolved to a filsys_id. */ int prefetch_filesys(struct query *q, char **argv, client *cl) { EXEC SQL BEGIN DECLARE SECTION; int fid, phid; EXEC SQL END DECLARE SECTION; int argc; fid = *(int *)argv[0]; EXEC SQL SELECT phys_id INTO :phid FROM filesys WHERE filsys_id = :fid; if (dbms_errno) return mr_errcode; argc = q->argc + q->vcnt; sprintf(argv[argc++], "%d", phid); sprintf(argv[argc], "%d", fid); return MR_SUCCESS; } /* setup_ghst(): */ int setup_ghst(struct query *q, char **argv, client *cl) { if (strcmp(argv[0], "*") || strcmp(argv[1], "*") || strcmp(argv[2], "*") || strcmp(argv[3], "*")) return MR_SUCCESS; else return MR_PERM; } /* setup_ahst(): */ int setup_ahst(struct query *q, char **argv, client *cl) { EXEC SQL BEGIN DECLARE SECTION; char *name, oldname[MACHINE_NAME_SIZE], vendor[MACHINE_VENDOR_SIZE]; char model[MACHINE_MODEL_SIZE], os[MACHINE_OS_SIZE]; int value, id, ssaddr, smask, shigh, slow, cnt; unsigned int saddr, mask, high, low; EXEC SQL END DECLARE SECTION; int row, idx; struct in_addr addr; id = *(int *)argv[0]; if (!strcmp(q->shortname, "uhst")) { row = 1; EXEC SQL SELECT name, vendor, model, os INTO :oldname, :vendor, :model, :os FROM machine WHERE mach_id = :id; } else row = 0; if (q->version < 6) idx = 0; else if (q->version >= 6 && q->version < 8) idx = 1; else idx = 2; /* Sanity check name, vendor, model, and os. */ if ((row == 0 || strcasecmp(argv[1], oldname)) && !hostname_check(argv[row])) return MR_BAD_CHAR; if ((row == 0 || strcasecmp(argv[2], vendor)) && !hostinfo_check(argv[row + 1], 0)) return MR_BAD_CHAR; if ((row == 0 || strcasecmp(argv[3], model)) && !hostinfo_check(argv[row + 2], 1)) return MR_BAD_CHAR; if ((row == 0 || strcasecmp(argv[4], os)) && !hostinfo_check(argv[row + 3], 0)) return MR_BAD_CHAR; /* check for duplicate name */ name = argv[row]; EXEC SQL SELECT count(mach_id) INTO :cnt FROM hostalias WHERE name = UPPER(:name); if (dbms_errno) return mr_errcode; if (cnt != 0) return MR_EXISTS; /* check address */ if (!strcmp(argv[9 + row + idx], "unassigned")) value = -1; else if (!strcmp(argv[9 + row + idx], "unique")) { if (*(int *)argv[8 + row + idx] == 0) value = -1; else value = -2; } else { value = ntohl(inet_addr(argv[9 + row + idx])); if (value == -1) return MR_ADDRESS; } if (value == 0) return MR_ADDRESS; if (value != -1) { /* * an address or unique was specified. */ id = *(int *)argv[8 + row + idx]; EXEC SQL SELECT saddr, mask, high, low INTO :ssaddr, :smask, :shigh, :slow FROM subnet WHERE snet_id = :id; if (dbms_errno) return mr_errcode; saddr = (unsigned) ssaddr; mask = (unsigned) smask; high = (unsigned) shigh; low = (unsigned) slow; if (value != -2) { /* * someone specified an IP address for the host record */ if ((value & mask) != saddr || value < low || value > high) return MR_ADDRESS; /* * run the address argument through inet_addr(). This * has the effect that any out of bounds host addrs will * be converted to a valid host addr. We do this now * so that the uniqueness check works. We should also * link in an inet_addr() that returns an error for * this case. */ addr.s_addr = inet_addr(argv[9 + row + idx]); name = inet_ntoa(addr); EXEC SQL SELECT count(mach_id) INTO :cnt FROM machine WHERE address = :name; if (dbms_errno) return mr_errcode; if (cnt > 0) { /* * make IP address is unique. If this a modify request * (row == 1), then we expect one record to exist. */ if (row == 0 || (row == 1 && cnt > 1)) return MR_ADDRESS; if (row == 1 && cnt == 1) { EXEC SQL SELECT mach_id INTO :id FROM machine WHERE address = :name; if (id != *(int *)argv[0]) return MR_ADDRESS; } } } else { /* * a "unique" address was specified. Walk through the * range specified in the network record, return * error if no room left. */ for (id = low; id <= high; id++) { if (((id & 0xff) == 0) || ((id & 0xff) == 255)) continue; addr.s_addr = htonl(id); name = inet_ntoa(addr); EXEC SQL SELECT count(mach_id) INTO :cnt FROM machine WHERE address = :name; if (dbms_errno) return mr_errcode; if (cnt == 0) break; } if (cnt != 0) return MR_NO_ID; else value = htonl(id); } strcpy(argv[9 + row + idx], name); } else strcpy(argv[9 + row + idx], "unassigned"); /* status checking */ value = atoi(argv[7 + row + idx]); if (row == 0 && !(value == 1 || value == 0)) return MR_TYPE; if (row == 1) { id = *(int *)argv[0]; EXEC SQL SELECT status INTO :cnt FROM machine WHERE mach_id = :id; if (dbms_errno) return mr_errcode; if (value != cnt) { EXEC SQL UPDATE machine SET statuschange = SYSDATE WHERE mach_id = :id; } } /* * If this is an update_host query, we're done. */ if (row == 1) return MR_SUCCESS; /* * For an add_host query, allocate and fill in a new machine id, * and then insert the creator id. */ if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS) return mr_errcode; sprintf(argv[q->argc + q->vcnt + 1], "%d", cl->client_id); return MR_SUCCESS; } /* setup_ahal(): */ int setup_ahal(struct query *q, char **argv, client *cl) { EXEC SQL BEGIN DECLARE SECTION; char *name; int cnt; EXEC SQL END DECLARE SECTION; char *p; name = argv[0]; if (!hostname_check(argv[0])) return MR_BAD_CHAR; EXEC SQL SELECT count(mach_id) INTO :cnt FROM machine WHERE name = UPPER(:name); if (dbms_errno) return mr_errcode; if (cnt > 0) return MR_EXISTS; return MR_SUCCESS; } /* setup_ahha(): Check characters in hwaddr, and make sure it's not * a duplicate. */ int setup_ahha(struct query *q, char **argv, client *cl) { EXEC SQL BEGIN DECLARE SECTION; char *hwaddr = argv[1]; int count; EXEC SQL END DECLARE SECTION; char *p; if (strlen(hwaddr) == 0) return MR_BAD_CHAR; if (*hwaddr && strcasecmp(hwaddr, "unknown")) { for (p = hwaddr; *p; p++) { if (isupper(*p)) *p = tolower(*p); if (!isxdigit(*p)) return MR_BAD_CHAR; } if (p != hwaddr + 12) return MR_ADDRESS; EXEC SQL SELECT COUNT(hwaddr) INTO :count FROM hwaddrmap WHERE hwaddr = :hwaddr; if (count) return MR_NOT_UNIQUE; } return MR_SUCCESS; } /* setup_aprn(): Make sure name/duplexname don't conflict with * anything. If [ANY] was specified for the spooling host, pick the * least loaded print server that serves this kind of printer. */ int setup_aprn(struct query *q, char **argv, client *cl) { int best = -1, row, idx = 0; char *p; EXEC SQL BEGIN DECLARE SECTION; int mid, usage, count; char types[STRINGS_STRING_SIZE], *hwaddr, *name, *duplexname, *oldname; char *service; EXEC SQL END DECLARE SECTION; /* Check for aprn or uprn. */ if (q->type == MR_Q_APPEND) row = 0; else row = 1; if (q->version >= 13) idx = 3; name = argv[PRN_NAME + row]; duplexname = argv[PRN_DUPLEXNAME + row]; oldname = argv[0]; if (!*name) return MR_BAD_CHAR; else { if (q->type == MR_Q_APPEND) { EXEC SQL SELECT COUNT(name) INTO :count FROM printers WHERE name = :name OR duplexname = :name; } else { EXEC SQL SELECT COUNT(name) INTO :count FROM printers WHERE ( name = :name OR duplexname = :name ) AND name != :oldname; } if (dbms_errno) return mr_errcode; if (count) return MR_NOT_UNIQUE; } if (*duplexname) { if (q->type == MR_Q_APPEND) { EXEC SQL SELECT COUNT(name) INTO :count FROM printers WHERE name = :duplexname OR duplexname = :duplexname; } else { EXEC SQL SELECT COUNT(name) INTO :count FROM printers WHERE ( name = :duplexname OR duplexname = :duplexname ) AND name != :oldname; } if (dbms_errno) return mr_errcode; if (count) return MR_NOT_UNIQUE; } if (!strcmp(name, duplexname)) return MR_NOT_UNIQUE; mid = *(int *)argv[6 + row + idx]; if (mid == -1) { if (!strcasecmp(argv[PRN_TYPE + row], "DORM") || !strcasecmp(argv[PRN_TYPE + row], "CLUSTER")) service = "CUPS-CLUSTER"; else service = "CUPS-PRINT"; EXEC SQL DECLARE csr_rm CURSOR FOR SELECT ps.mach_id, s.string FROM printservers ps, strings s WHERE ps.mach_id IN ( SELECT mach_id FROM serverhosts WHERE service = :service AND enable = 1 ) AND ps.printer_types = s.string_id; if (dbms_errno) return mr_errcode; EXEC SQL OPEN csr_rm; if (dbms_errno) return mr_errcode; while (1) { EXEC SQL FETCH csr_rm INTO :mid, :types; if (sqlca.sqlcode) break; for (p = strtok(types, ", "); p; p = strtok(NULL, ", ")) { if (!strcasecmp(argv[PRN_TYPE + row], p)) { EXEC SQL SELECT COUNT(name) INTO :usage FROM printers WHERE rm = :mid; if (best < 0 || usage < best) { best = usage; *(int *)argv[6 + row + idx] = mid; break; } } } } EXEC SQL CLOSE csr_rm; if (dbms_errno) return mr_errcode; if (best == -1) return MR_SERVICE; } else { EXEC SQL SELECT mach_id INTO :mid FROM printservers WHERE mach_id = :mid; if (sqlca.sqlcode) return MR_SERVICE; } return MR_SUCCESS; } int setup_dpsv(struct query *q, char **argv, client *cl) { int id; EXEC SQL BEGIN DECLARE SECTION; int cnt; EXEC SQL END DECLARE SECTION; id = *(int *)argv[0]; EXEC SQL SELECT COUNT(rm) INTO :cnt FROM printers WHERE rm = :id; if (cnt > 0) return MR_IN_USE; return MR_SUCCESS; } int setup_dcon(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int id, cnt; char containername[CONTAINERS_NAME_SIZE]; EXEC SQL END DECLARE SECTION; id = *(int *)argv[0]; /* check to see if there are machines in this container */ EXEC SQL SELECT COUNT(mach_id) INTO :cnt FROM mcntmap WHERE cnt_id = :id; if (cnt > 0) return MR_IN_USE; /* check to see if there are subcontainers in this container */ /* get the container name */ EXEC SQL SELECT name INTO :containername FROM containers WHERE cnt_id = :id; /* trim off the trailing spaces */ strcpy(containername, strtrim(containername)); EXEC SQL SELECT COUNT(cnt_id) INTO :cnt FROM containers WHERE LOWER(name) LIKE LOWER(:containername || '/' || '%'); if (cnt > 0) return MR_IN_USE; if (dbms_errno) return mr_errcode; return MR_SUCCESS; } int setup_scli(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int cnt_id, list_id; EXEC SQL END DECLARE SECTION; cnt_id = *(int *)argv[0]; /* Check if someone has already set the list for this container */ EXEC SQL SELECT list_id INTO :list_id FROM containers WHERE cnt_id = :cnt_id; if (list_id != 0) return MR_EXISTS; if (dbms_errno) return mr_errcode; return MR_SUCCESS; } /* hostname_check() * validate the rfc1035/rfc1123-ness of a hostname */ int hostname_check(char *name) { char *p; int count; /* Sanity check name: must contain only letters, numerals, and * hyphen, and not start or end with a hyphen. Also make sure no * label (the thing the .s seperate) is longer than 63 characters, * or empty. */ for (p = name, count = 0; *p; p++) { count++; if ((!isalnum(*p) && *p != '-' && *p != '.') || (*p == '-' && p[1] == '.')) return 0; if (*p == '.') { if (count == 1) return 0; count = 0; } if (count == 64) return 0; } if (*(p - 1) == '-') return 0; return 1; } int hostinfo_check(char *info, int num) { char *p; if (!*info) return 1; /* Sanity check host hostinfo: must start with a letter (or number * if num is true), contain only letters, numerals, and hyphen, and * not end with a hyphen. */ if (!isalpha(*info) && (!num || !isdigit(*info))) return 0; for (p = info; *p; p++) { if ((!isalnum(*p) && *p != '-' && *p != '.') || (*p == '-' && p[1] == '.')) return 0; } if (!isalnum(*(p - 1))) return 1; return 1; } int setup_aali(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int cnt; char *name, *type, *trans; EXEC SQL END DECLARE SECTION; name = argv[0]; type = argv[1]; trans = argv[2]; if (strcmp(strtrim(type), "FILESYS")) return MR_SUCCESS; EXEC SQL SELECT count(label) INTO :cnt FROM filesys WHERE label = :name; if (cnt > 0) return MR_EXISTS; return MR_SUCCESS; } int setup_acon(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; char containername[CONTAINERS_NAME_SIZE]; EXEC SQL END DECLARE SECTION; char* ptr; memset(containername, 0, sizeof(containername)); strcpy(containername, argv[0]); ptr = strrchr(containername, '/'); /* sub container, check for parents */ if (ptr) { *ptr = '\0'; EXEC SQL SELECT * FROM containers WHERE lower(name) = lower(:containername); if (sqlca.sqlerrd[2] != 1) return MR_CONTAINER_NO_PARENT; } if ((mr_errcode = prefetch_value(q, argv, cl)) != MR_SUCCESS) return mr_errcode; return MR_SUCCESS; } int check_mailman_listname(char *name, const char *suffix) { char *p; EXEC SQL BEGIN DECLARE SECTION; int i, cnt; EXEC SQL END DECLARE SECTION; p = strstr(name, suffix); if (p) { if (strlen(name) == (p - name + strlen(suffix))) { /* list is of the form "name-suffix" */ i = (p - name); EXEC SQL SELECT COUNT(name) INTO :cnt FROM list WHERE name = SUBSTR(:name, 1, :i) AND mailman = 1; if (cnt > 0) return MR_EXISTS; } } return MR_SUCCESS; }