X-Git-Url: http://andersk.mit.edu/gitweb/moira.git/blobdiff_plain/263a36d43d98dd26dde954572df926055876d7a6..4656d65034eae549c3b63e37b708e63ad55a0b1b:/server/qrtn.pc diff --git a/server/qrtn.pc b/server/qrtn.pc index b720b7cf..a7c0ddf6 100644 --- a/server/qrtn.pc +++ b/server/qrtn.pc @@ -24,8 +24,8 @@ RCSID("$Header$"); SQLDA *mr_sqlda; EXEC SQL BEGIN DECLARE SECTION; -int mr_sig_length; char stmt_buf[MR_STMTBUF_LEN]; +int proxy_acl; EXEC SQL END DECLARE SECTION; char *Argv[QMAXARGS]; @@ -40,6 +40,11 @@ char *database = "moira"; EXEC SQL END DECLARE SECTION; extern char *whoami; extern FILE *journal; +extern int QueryCount, max_version; +extern struct query Queries[]; + +/* Put this in a variable so that we can patch it if necessary */ +int max_row_count = 8192; int mr_verify_query(client *cl, struct query *q, int argc, char *argv_ro[]); int do_retrieve(struct query *q, char *pqual, @@ -83,20 +88,19 @@ EXEC SQL WHENEVER SQLERROR DO dbmserr(); int mr_open_database(void) { int i; - static first_open = 1; + static int first_open = 1; if (first_open) { first_open = 0; /* initialize local argv */ - for (i = 0; i < 16; i++) + for (i = 0; i < QMAXARGS; i++) Argv[i] = xmalloc(MAX_FIELD_WIDTH); mr_sqlda = mr_alloc_sqlda(); incremental_init(); - flush_cache(); } dbms_errno = 0; @@ -108,9 +112,8 @@ int mr_open_database(void) if (dbms_errno) return mr_errcode; - EXEC SQL SELECT data_length INTO :mr_sig_length FROM user_tab_columns - WHERE table_name = 'USERS' and column_name = 'SIGNATURE'; - EXEC SQL COMMIT WORK; + EXEC SQL SELECT list_id INTO :proxy_acl FROM capacls + WHERE capability = 'proxy'; if (dbms_errno) return mr_errcode; @@ -119,7 +122,6 @@ int mr_open_database(void) void mr_close_database(void) { - flush_cache(); EXEC SQL COMMIT RELEASE; } @@ -130,7 +132,7 @@ int mr_check_access(client *cl, char *name, int argc, char *argv_ro[]) dbms_errno = 0; mr_errcode = 0; - q = get_query_by_name(name); + q = get_query_by_name(name, cl->version); if (!q) return MR_NO_HANDLE; @@ -155,7 +157,7 @@ int mr_process_query(client *cl, char *name, int argc, char *argv_ro[], /* list queries command */ if (!strcmp(name, "_list_queries")) { - list_queries(action, actarg); + list_queries(cl, action, actarg); return MR_SUCCESS; } @@ -164,7 +166,7 @@ int mr_process_query(client *cl, char *name, int argc, char *argv_ro[], { if (argc < 1) return MR_ARGS; - q = get_query_by_name(argv_ro[0]); + q = get_query_by_name(argv_ro[0], cl->version); if (!q) return MR_NO_HANDLE; help_query(q, action, actarg); @@ -172,7 +174,7 @@ int mr_process_query(client *cl, char *name, int argc, char *argv_ro[], } /* get query structure, return error if named query does not exist */ - q = get_query_by_name(name); + q = get_query_by_name(name, cl->version); if (!q) return MR_NO_HANDLE; v = q->validate; @@ -216,6 +218,15 @@ int mr_process_query(client *cl, char *name, int argc, char *argv_ro[], status = do_retrieve(q, qual, sq_save_args, sq); if (status != MR_SUCCESS) { + char **argv; + int i; + + while (sq_get_data(sq, &argv)) + { + for (i = 0; i < q->vcnt; i++) + free(argv[i]); + free(argv); + } sq_destroy(sq); break; } @@ -253,7 +264,6 @@ int mr_process_query(client *cl, char *name, int argc, char *argv_ro[], incremental_after(q->rtable, qual, argv_ro); if (status != MR_SUCCESS) break; - flush_name(argv_ro[0], q->rtable); table = table_name[q->rtable]; if (strcmp(q->shortname, "sshi") && strcmp(q->shortname, "ssif")) { @@ -332,7 +342,6 @@ int mr_process_query(client *cl, char *name, int argc, char *argv_ro[], incremental_clear_after(); if (status != MR_SUCCESS) break; - flush_name(argv_ro[0], q->rtable); EXEC SQL UPDATE tblstats SET deletes = deletes + 1, modtime = SYSDATE WHERE table_name = :table; @@ -388,12 +397,10 @@ out: } else { - cache_abort(); EXEC SQL ROLLBACK WORK; incremental_flush(); } } - cache_commit(); /* commit following abort is safe */ if (status != MR_SUCCESS) com_err(whoami, status, " (Query failed)"); @@ -418,7 +425,8 @@ char *build_qual(char *fmt_buf, int argc, char *argv[]) while (*fmt) { - if (!like && !arg) + + if ((!like && !arg) || argc == 0) { /* only plain text remains */ strcpy(res, fmt); @@ -459,7 +467,8 @@ char *build_qual(char *fmt_buf, int argc, char *argv[]) arg = strchr(fmt, '%'); } else { /* LIKE arg: copy over up to the arg, then copy and convert arg */ - int escape = 0; + int escape = 0, pattern = 0; + char *likepos = res + (like - fmt); strncpy(res, fmt, arg - fmt); res += arg - fmt; @@ -473,10 +482,12 @@ char *build_qual(char *fmt_buf, int argc, char *argv[]) case '*': *res++ = '%'; *res++ = '%'; /* need to double for build_sql_stmt */ + pattern = 1; break; case '?': *res++ = '_'; + pattern = 1; break; case '%': @@ -497,8 +508,12 @@ char *build_qual(char *fmt_buf, int argc, char *argv[]) } } + /* if no pattern characters, write over "LIKE" with " = " */ + if (!pattern && !escape) + memcpy(likepos, " = ", 4); + fmt = arg + 2; - if (*fmt == '\'') + while (*fmt && *fmt != ' ') *res++ = *fmt++; if (escape) @@ -549,12 +564,14 @@ int mr_verify_query(client *cl, struct query *q, int argc, char *argv_ro[]) return MR_BAD_CHAR; } - /* check initial query access */ + /* Check initial query access. If we're acting as a proxy, only allow + * access if the query has "default" as a capacl. + */ status = check_query_access(q, Argv, cl); if (status != MR_SUCCESS && status != MR_PERM) return status; - if (status == MR_SUCCESS) - privileged++; + if (status == MR_SUCCESS && (!cl->proxy_id || q->everybody)) + privileged++; /* validate arguments */ if (v && v->valobj) @@ -577,11 +594,6 @@ int mr_verify_query(client *cl, struct query *q, int argc, char *argv_ro[]) return privileged ? MR_SUCCESS : MR_PERM; } - -/* This routine caches info from the database. Each query acl is stored - * in the query structure, and whether that acl contains everybody. - */ - int check_query_access(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; @@ -594,28 +606,22 @@ int check_query_access(struct query *q, char *argv[], client *cl) if (def_uid == 0) EXEC SQL SELECT users_id INTO :def_uid FROM users WHERE login = 'default'; - /* get query access control list */ - if (q->acl != 0) - acl_id = q->acl; + name = q->shortname; + EXEC SQL SELECT list_id INTO :acl_id FROM capacls WHERE tag = :name; + if (sqlca.sqlcode < 0) + return MR_DBMS_ERR; + if (sqlca.sqlcode == SQL_NO_MATCH) + return MR_PERM; + q->acl = acl_id; + + /* check for default access */ + EXEC SQL SELECT member_id INTO :acl_id FROM imembers + WHERE list_id = :acl_id AND member_type = 'USER' + AND member_id = :def_uid; + if (sqlca.sqlerrd[2] == 0) + q->everybody = 0; else - { - name = q->shortname; - EXEC SQL SELECT list_id INTO :acl_id FROM capacls WHERE tag = :name; - if (sqlca.sqlcode < 0) - return MR_DBMS_ERR; - if (sqlca.sqlcode == SQL_NO_MATCH) - return MR_PERM; - q->acl = acl_id; - - /* check for default access */ - EXEC SQL SELECT member_id INTO :acl_id FROM imembers - WHERE list_id = :acl_id AND member_type = 'USER' - AND member_id = :def_uid; - if (sqlca.sqlerrd[2] == 0) - q->everybody = 0; - else - q->everybody = 1; - } + q->everybody = 1; if (q->everybody) return MR_SUCCESS; @@ -626,7 +632,6 @@ int check_query_access(struct query *q, char *argv[], client *cl) return MR_PERM; } - int find_member(char *list_type, int list_id, client *cl) { EXEC SQL BEGIN DECLARE SECTION; @@ -775,6 +780,10 @@ int set_next_object_id(char *object, enum tables table, int limit) starting_value = value; while (1) { +#ifdef ULTRIX_ID_HOLE + if (limit && value > 31999 && value < 32768) + value = 32768; +#endif if (limit && value > MAX_ID_VALUE) value = MIN_ID_VALUE; @@ -850,23 +859,18 @@ int set_krb_mapping(char *name, char *login, int ok, int *kid, int *uid) } -/* For now this just checks the argc's. It should also see that there - * are no duplicate names. - */ - void sanity_check_queries(void) { int i; int maxv = 0, maxa = 0; - extern int QueryCount2; - extern struct query Queries2[]; #define MAX(x, y) ((x) > (y) ? (x) : (y)) - for (i = 0; i < QueryCount2; i++) + for (i = 0; i < QueryCount; i++) { - maxv = MAX(maxv, Queries2[i].vcnt); - maxa = MAX(maxa, Queries2[i].argc); + maxv = MAX(maxv, Queries[i].vcnt); + maxa = MAX(maxa, Queries[i].argc); + max_version = MAX(max_version, Queries[i].version); } if (MAX(maxv, maxa) > QMAXARGS) { @@ -937,7 +941,7 @@ int do_for_all_rows(char *query, int count, mr_sqlda->L[i] = MAX_FIELD_WIDTH; } - while (1) + while (rowcount < max_row_count) { EXEC SQL FETCH curs USING DESCRIPTOR mr_sqlda; if (sqlca.sqlcode) @@ -949,8 +953,142 @@ int do_for_all_rows(char *query, int count, if (mr_errcode) return mr_errcode; - return (rowcount == 0) ? MR_NO_MATCH : MR_SUCCESS; + if (rowcount == max_row_count) + { + critical_alert("moirad", "attempted query with too many rows"); + return MR_NO_MEM; + } + else if (rowcount == 0) + return MR_NO_MATCH; + else + return MR_SUCCESS; +} + +/* Do a name to ID translation. Moved from cache.pc because we did away + * with the cache, but what this function does is still useful to us. + */ + +int name_to_id(char *name, enum tables type, int *id) +{ + EXEC SQL BEGIN DECLARE SECTION; + char *iname; + int j; + EXEC SQL END DECLARE SECTION; + + iname = name; + + switch (type) + { + case USERS_TABLE: + if (strchr(iname, '@') || (strlen(iname) > 8)) + { + sqlca.sqlcode = SQL_NO_MATCH; + break; + } + EXEC SQL SELECT users_id INTO :j FROM users WHERE login = :iname; + break; + case LIST_TABLE: + EXEC SQL SELECT list_id INTO :j FROM list WHERE name = :iname; + break; + case MACHINE_TABLE: + EXEC SQL SELECT mach_id INTO :j FROM machine WHERE name = UPPER(:iname); + break; + case SUBNET_TABLE: + EXEC SQL SELECT snet_id INTO :j FROM subnet WHERE name = UPPER(:iname); + break; + case CLUSTERS_TABLE: + EXEC SQL SELECT clu_id INTO :j FROM clusters WHERE name = :iname; + break; + case FILESYS_TABLE: + EXEC SQL SELECT filsys_id INTO :j FROM filesys WHERE label = :iname; + break; + case STRINGS_TABLE: + if (!iname[0]) /* special-case empty string */ + { + *id = 0; + return MR_SUCCESS; + } + EXEC SQL SELECT string_id INTO :j FROM strings WHERE string = :iname; + break; + case CONTAINERS_TABLE: + EXEC SQL SELECT cnt_id INTO :j FROM containers WHERE LOWER(name) = + LOWER(:iname); + break; + default: + return MR_INTERNAL; + } + if (sqlca.sqlcode == SQL_NO_MATCH) + return MR_NO_MATCH; + if (sqlca.sqlerrd[2] > 1) + return MR_NOT_UNIQUE; + if (sqlca.sqlcode) + return MR_DBMS_ERR; + *id = j; + + return MR_SUCCESS; } +/* Perform an ID to name mapping. name should be a pointer to a pointer to + * malloc'ed data. The buffer it refers to will be freed, and a new buffer + * allocated with the answer. + * + * This used to be in cache.pc, but we've removed the cache, and this function + * is still useful to us. + */ + +int id_to_name(int id, enum tables type, char **name) +{ + EXEC SQL BEGIN DECLARE SECTION; + char iname[MAX_FIELD_WIDTH]; + int j; + EXEC SQL END DECLARE SECTION; + + j = id; + + switch (type) + { + case USERS_TABLE: + EXEC SQL SELECT login INTO :iname FROM users WHERE users_id = :j; + break; + case LIST_TABLE: + EXEC SQL SELECT name INTO :iname FROM list WHERE list_id = :j; + break; + case MACHINE_TABLE: + EXEC SQL SELECT name INTO :iname FROM machine WHERE mach_id = :j; + break; + case SUBNET_TABLE: + EXEC SQL SELECT name INTO :iname FROM subnet WHERE snet_id = :j; + break; + case CLUSTERS_TABLE: + EXEC SQL SELECT name INTO :iname FROM clusters WHERE clu_id = :j; + break; + case FILESYS_TABLE: + EXEC SQL SELECT label INTO :iname FROM filesys WHERE filsys_id = :j; + break; + case STRINGS_TABLE: + EXEC SQL SELECT string INTO :iname FROM strings WHERE string_id = :j; + break; + case CONTAINERS_TABLE: + EXEC SQL SELECT name INTO :iname FROM containers WHERE cnt_id = :j; + break; + default: + return MR_INTERNAL; + } + if (sqlca.sqlcode == SQL_NO_MATCH) + { + free(*name); + sprintf(iname, "#%d", j); + *name = xstrdup(iname); + return MR_NO_MATCH; + } + if (sqlca.sqlerrd[2] > 1) + return MR_INTERNAL; + if (sqlca.sqlcode) + return MR_DBMS_ERR; + free(*name); + *name = xstrdup(strtrim(iname)); + + return MR_SUCCESS; +} /* eof:qrtn.dc */