/* $Id$ * * Check access to queries * * 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 "qrtn.h" #include "query.h" #include #include #include #include #include #include EXEC SQL INCLUDE sqlca; RCSID("$Header$"); extern char *whoami; extern int dbms_errno, mr_errcode; EXEC SQL WHENEVER SQLERROR DO dbmserr(); /* Specialized Access Routines */ /* access_user - verify that client name equals specified login name * * - since field validation routines are called first, a users_id is * now in argv[0] instead of the login name. */ int access_user(struct query *q, char *argv[], client *cl) { if (cl->users_id != *(int *)argv[0]) return MR_PERM; else return MR_SUCCESS; } int access_update_user(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int users_id, unix_uid, status, comments, secure; char login[USERS_LOGIN_SIZE], shell[USERS_SHELL_SIZE]; char winconsoleshell[USERS_WINCONSOLESHELL_SIZE], last[USERS_LAST_SIZE]; char first[USERS_FIRST_SIZE], middle[USERS_MIDDLE_SIZE]; char clearid[USERS_CLEARID_SIZE], type[USERS_TYPE_SIZE]; char signature[USERS_SIGNATURE_SIZE]; EXEC SQL END DECLARE SECTION; /* The two fields we let users update themselves didn't appear until * version 11. */ if (q->version < 11) return MR_PERM; if (cl->users_id != *(int *)argv[0]) return MR_PERM; users_id = *(int *)argv[0]; EXEC SQL SELECT u.login, u.unix_uid, u.shell, u.winconsoleshell, u.last, u.first, u.middle, u.status, u.clearid, u.type, u.comments, u.signature, u.secure INTO :login, :unix_uid, :shell, :winconsoleshell, :last, :first, :middle, :status, :clearid, :type, :comments, :signature, :secure FROM USERS u WHERE u.users_id = :users_id; /* None of these things can have changed. */ if (strcmp(argv[1], strtrim(login)) || (unix_uid != atoi(argv[2])) || strcmp(argv[3], strtrim(shell)) || strcmp(argv[4], strtrim(winconsoleshell)) || strcmp(argv[5], strtrim(last)) || strcmp(argv[6], strtrim(first)) || strcmp(argv[7], strtrim(middle)) || (status != atoi(argv[8])) || strcmp(argv[9], strtrim(clearid)) || strcmp(argv[10], strtrim(type)) || (comments != *(int *)argv[11]) || strcmp(argv[12], strtrim(signature)) || (secure != atoi(argv[13]))) return MR_PERM; return MR_SUCCESS; } /* access_login - verify that client name equals specified login name * * argv[0...n] contain search info. q-> */ int access_login(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int id; EXEC SQL END DECLARE SECTION; if (q->argc != 1) return MR_ARGS; if (!strcmp(q->shortname, "gual")) { EXEC SQL SELECT users_id INTO :id FROM users WHERE login = :argv[0] AND users_id != 0; } else if (!strcmp(q->shortname, "gubl")) { EXEC SQL SELECT users_id INTO :id FROM users u WHERE u.login = :argv[0] AND u.users_id != 0; } else if (!strcmp(q->shortname, "guau")) { EXEC SQL SELECT users_id INTO :id FROM users WHERE unix_uid = :argv[0] AND users_id != 0; } else if (!strcmp(q->shortname, "gubu")) { EXEC SQL SELECT users_id INTO :id FROM users u WHERE u.unix_uid = :argv[0] AND u.users_id != 0; } if (sqlca.sqlcode == SQL_NO_MATCH) return MR_NO_MATCH; /* ought to be MR_USER, but this is what gual returns, so we have to be consistent */ else if (sqlca.sqlerrd[2] != 1 || id != cl->users_id) return MR_PERM; else return MR_SUCCESS; } /* access_spob - check access for set_pobox */ int access_spob(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int id; EXEC SQL END DECLARE SECTION; int status; if (!strcmp(argv[1], "IMAP")) { EXEC SQL SELECT owner INTO :id FROM filesys f WHERE f.label = :argv[2] AND f.type = 'IMAP' AND f.lockertype = 'USER'; if (cl->users_id != id) return MR_PERM; } /* Non-query owners can't forward mail to a POSTOFFICE or MAILHUB server, * nor to a nonresolving domain. */ if (!strcmp(argv[1], "SMTP") || !strcmp(argv[1], "SPLIT")) { status = check_mail_string(argv[2]); if (status) return status; } if (cl->users_id != *(int *)argv[0]) return MR_PERM; else return MR_SUCCESS; } /* access_list - check access for most list operations * * Inputs: argv[0] - list_id * q - query name * argv[2] - member ID (only for queries "amtl" and "dmfl") * argv[7] - group ID (only for query "ulis") * cl - client name * * - check that client is a member of the access control list * - OR, if the query is add_member_to_list or delete_member_from_list * and the list is public, allow access if client = member */ int access_list(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int list_id, acl_id, flags, gid, users_id, member_id, member_acl_id; int memacl_id, mailman, mailman_id; char acl_type[LIST_ACL_TYPE_SIZE], name[LIST_NAME_SIZE], *newname; char member_acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE]; EXEC SQL END DECLARE SECTION; int status, cnt; char *buf; list_id = *(int *)argv[0]; member_id = *(int *)argv[2]; EXEC SQL SELECT acl_id, acl_type, memacl_id, memacl_type, gid, publicflg, name, mailman, mailman_id INTO :acl_id, :acl_type, :memacl_id, :memacl_type, :gid, :flags, :name, :mailman, :mailman_id FROM list WHERE list_id = :list_id; if (sqlca.sqlerrd[2] != 1) return MR_INTERNAL; /* if update_list, don't allow them to change the GID or rename to a username other than their own */ if (!strcmp("ulis", q->shortname)) { if (!strcmp(argv[7], UNIQUE_GID)) { if (gid != -1) return MR_PERM; } else { if (gid != atoi(argv[7])) return MR_PERM; } newname = argv[1]; /* Check that it doesn't conflict with the Grouper namespace. */ if (strlen(newname) > 4 && isdigit(newname[2]) && isdigit(newname[3]) && newname[4] == '-') { if (!strncasecmp(newname, "fa", 2) || !strncasecmp(newname, "sp", 2) || !strncasecmp(newname, "su", 2) || !strncasecmp(newname, "ja", 2)) return MR_RESERVED; } /* Don't let anyone take owner-foo list names. They interact * weirdly with the aliases automatically generated by * mailhub.gen. */ if (!strncasecmp(newname, "owner-", 6)) return MR_RESERVED; EXEC SQL SELECT users_id INTO :users_id FROM users WHERE login = :newname; if ((sqlca.sqlcode != SQL_NO_MATCH) && strcmp(strtrim(name), newname) && (users_id != cl->users_id)) return MR_PERM; /* For modern enough clients, don't allow ordinary users to toggle * the mailman bit or change the server. */ if (q->version >= 10) { if (mailman != atoi(argv[9])) return MR_PERM; if (mailman_id != *(int *)argv[10]) return MR_PERM; } } /* Don't allow non-query owners to add STRINGs to lists if they end * in a domain that's MIT.EDU or one of the hosts that provide the * MAILHUB or POSTOFFICE services. */ if (!strcmp(q->shortname, "amtl") || !strcmp(q->shortname, "atml")) { if (!strcmp("STRING", argv[1])) { buf = malloc(0); status = id_to_name(*(int *)argv[2], STRINGS_TABLE, &buf); if (status) return status; status = check_mail_string(buf); free(buf); if (status) return status; } } /* check for client in access control list and return success right * away if it's there. */ if (find_member(acl_type, acl_id, cl)) return MR_SUCCESS; /* If not amtl, atml, or dmfl, we lose. */ if (strcmp(q->shortname, "amtl") && strcmp(q->shortname, "atml") && strcmp(q->shortname, "dmfl") && strcmp(q->shortname, "tmol")) return MR_PERM; if (find_member(memacl_type, memacl_id, cl)) return MR_SUCCESS; if (flags || q->type == MR_Q_DELETE) { if (!strcmp("USER", argv[1]) && *(int *)argv[2] == cl->users_id) return MR_SUCCESS; if (!strcmp("KERBEROS", argv[1]) && *(int *)argv[2] == -cl->client_id) return MR_SUCCESS; if (!strcmp("LIST", argv[1]) && !strcmp("dmfl", q->shortname)) { EXEC SQL SELECT acl_id, acl_type INTO :member_acl_id, :member_acl_type FROM list WHERE list_id = :member_id; if (find_member(member_acl_type, member_acl_id, cl)) return MR_SUCCESS; } } /* Otherwise fail. */ return MR_PERM; } /* access_visible_list - allow access to list only if it is not hidden, * or if the client is on the ACL * * Inputs: argv[0] - list_id * cl - client identifier */ int access_visible_list(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int list_id, acl_id, memacl_id, flags ; char acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE]; EXEC SQL END DECLARE SECTION; int status; list_id = *(int *)argv[0]; EXEC SQL SELECT hidden, acl_id, acl_type, memacl_id, memacl_type INTO :flags, :acl_id, :acl_type, :memacl_id, :memacl_type FROM list WHERE list_id = :list_id; if (sqlca.sqlerrd[2] != 1) return MR_INTERNAL; if (!flags) return MR_SUCCESS; /* check for client in access control list */ status = find_member(acl_type, acl_id, cl); if (!status) { status = find_member(memacl_type, memacl_id, cl); if (!status) return MR_PERM; } return MR_SUCCESS; } /* access_vis_list_by_name - allow access to list only if it is not hidden, * or if the client is on the ACL * * Inputs: argv[0] - list name * cl - client identifier */ int access_vis_list_by_name(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int acl_id, memacl_id, flags, rowcount, list_id; char acl_type[LIST_ACL_TYPE_SIZE], memacl_type[LIST_ACL_TYPE_SIZE]; char *listname; EXEC SQL END DECLARE SECTION; int status; listname = argv[0]; EXEC SQL SELECT hidden, acl_id, acl_type, memacl_id, memacl_type, list_id INTO :flags, :acl_id, :acl_type, :memacl_id, :memacl_type, :list_id FROM list WHERE name = :listname; rowcount = sqlca.sqlerrd[2]; if (rowcount > 1) return MR_WILDCARD; if (rowcount == 0) return MR_NO_MATCH; if (!flags) return MR_SUCCESS; /* If the user is a member of the acl, memacl, or the list itself, * accept them. */ status = find_member(acl_type, acl_id, cl); if (!status) status = find_member(memacl_type, memacl_id, cl); if (!status) status = find_member("LIST", list_id, cl); if (!status) return MR_PERM; return MR_SUCCESS; } /* access_member - allow user to access member of type "USER" and name matches * username, or to access member of type "KERBEROS" and the principal matches * the user, or to access member of type "LIST" and list is one that user is * on the acl of, or the list is visible. Allow anyone to look up list * memberships of MACHINEs. */ int access_member(struct query *q, char *argv[], client *cl) { if (!strcmp(argv[0], "LIST") || !strcmp(argv[0], "RLIST")) return access_visible_list(q, &argv[1], cl); if (!strcmp(argv[0], "USER") || !strcmp(argv[0], "RUSER")) { if (cl->users_id == *(int *)argv[1]) return MR_SUCCESS; } if (!strcmp(argv[0], "KERBEROS") || !strcmp(argv[0], "RKERBEROS")) { if (cl->client_id == -*(int *)argv[1]) return MR_SUCCESS; } if (!strcmp(argv[0], "MACHINE") || !strcmp(argv[0], "RMACHINE")) return MR_SUCCESS; return MR_PERM; } /* access_qgli - special access routine for Qualified_get_lists. Allows * access iff argv[0] == "TRUE" and argv[2] == "FALSE". */ int access_qgli(struct query *q, char *argv[], client *cl) { if (!strcmp(argv[0], "TRUE") && !strcmp(argv[2], "FALSE")) return MR_SUCCESS; return MR_PERM; } /* access_service - allow access if user is on ACL of service. Don't * allow access if a wildcard is used. */ int access_service(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int acl_id; char *name, acl_type[LIST_ACL_TYPE_SIZE]; EXEC SQL END DECLARE SECTION; int status; char *c; name = argv[0]; for (c = name; *c; c++) { if (islower(*c)) *c = toupper(*c); } EXEC SQL SELECT acl_id, acl_type INTO :acl_id, :acl_type FROM servers WHERE name = :name; if (sqlca.sqlerrd[2] > 1) return MR_PERM; /* check for client in access control list */ status = find_member(acl_type, acl_id, cl); if (!status) return MR_PERM; return MR_SUCCESS; } /* access_filesys - verify that client is owner or on owners list of filesystem * named by argv[0] */ int access_filesys(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int users_id, list_id; char *name; EXEC SQL END DECLARE SECTION; int status; name = argv[0]; EXEC SQL SELECT owner, owners INTO :users_id, :list_id FROM filesys WHERE label = :name; if (sqlca.sqlerrd[2] != 1) return MR_PERM; if (users_id == cl->users_id) return MR_SUCCESS; status = find_member("LIST", list_id, cl); if (status) return MR_SUCCESS; else return MR_PERM; } /* access_host - successful if owner of host, or subnet containing host */ int access_host(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int mid, sid, id, subnet_status; char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE]; char *account_number; EXEC SQL END DECLARE SECTION; int status, idx; if (q->version < 6) idx = 0; else if (q->version >= 6 && q->version < 8) idx = 1; else idx = 2; if (q->type == MR_Q_APPEND) { /* Non-query owner must set use to zero */ if (atoi(argv[6 + idx]) != 0) return MR_PERM; /* ... and start the hostname with a letter */ if (isdigit(argv[0][0])) return MR_BAD_CHAR; id = *(int *)argv[8 + idx]; EXEC SQL SELECT s.owner_type, s.owner_id, s.status INTO :stype, :sid, :subnet_status FROM subnet s WHERE s.snet_id = :id; mid = 0; /* Non query owner must provide valid billing information. */ if (q->version >= 8) { if (subnet_status == SNET_STATUS_BILLABLE) { account_number = argv[7]; EXEC SQL SELECT account_number FROM accountnumbers WHERE account_number = :account_number; if (sqlca.sqlcode == SQL_NO_MATCH) return MR_ACCOUNT_NUMBER; } } if (find_member(stype, sid, cl)) return MR_SUCCESS; else return MR_PERM; } else /* q-type == MR_Q_UPDATE */ { EXEC SQL BEGIN DECLARE SECTION; int status, acomment, use, ocomment, snid; char contact[MACHINE_CONTACT_SIZE], address[MACHINE_ADDRESS_SIZE]; char name[MACHINE_NAME_SIZE]; char billing_contact[MACHINE_BILLING_CONTACT_SIZE]; EXEC SQL END DECLARE SECTION; id = *(int *)argv[0]; EXEC SQL SELECT m.name, m.use, m.contact, m.billing_contact, m.status, m.address, m.owner_type, m.owner_id, m.acomment, m.ocomment, m.snet_id, s.owner_type, s.owner_id, s.status INTO :name, :use, :contact, :billing_contact, :status, :address, :mtype, :mid, :acomment, :ocomment, :snid, :stype, :sid, :subnet_status FROM machine m, subnet s WHERE m.mach_id = :id AND s.snet_id = m.snet_id; if (dbms_errno) return mr_errcode; /* Non query owner must provide valid billing information. */ if (q->version >= 8) { if ((subnet_status == SNET_STATUS_BILLABLE) && (atoi(argv[10]) != 3)) { account_number = argv[8]; EXEC SQL SELECT account_number FROM accountnumbers WHERE account_number = :account_number; if (sqlca.sqlcode == SQL_NO_MATCH) return MR_ACCOUNT_NUMBER; } } /* non-query-owner cannot change use or ocomment */ if ((use != atoi(argv[7 + idx])) || (ocomment != *(int *)argv[14 + idx])) return MR_PERM; /* or rename to start with digit */ if (isdigit(argv[1][0]) && strcmp(strtrim(name), argv[1])) return MR_BAD_CHAR; if (!find_member(stype, sid, cl)) { if (find_member(mtype, mid, cl)) { /* host owner also cannot change contact, status, address, owner, or acomment */ if (strcmp(argv[6], strtrim(contact)) || (status != atoi(argv[8 + idx])) || strcmp(argv[10 + idx], strtrim(address)) || strcmp(argv[11 + idx], strtrim(mtype)) || (mid != *(int *)argv[12 + idx]) || (acomment != *(int *)argv[13 + idx])) return MR_PERM; /* Billing contact field didn't appear until version 6 */ if (q->version >= 6) if (strcmp(argv[7], strtrim(billing_contact))) return MR_PERM; } else return MR_PERM; } /* If moving to a new subnet, make sure user is on acl there */ id = *(int *)argv[9 + idx]; if (id != snid) { EXEC SQL SELECT owner_type, owner_id INTO :stype, :sid FROM subnet WHERE snet_id=:id; if (!find_member(stype, sid, cl)) return MR_PERM; } return MR_SUCCESS; } } /* access_ahal - check for adding a host alias. * successful if host has less then 2 aliases and (client is owner of * host or subnet). * If deleting an alias, any owner will do. */ int access_ahal(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int cnt, id, mid, sid; char mtype[MACHINE_OWNER_TYPE_SIZE], stype[SUBNET_OWNER_TYPE_SIZE]; EXEC SQL END DECLARE SECTION; int status; if (q->type == MR_Q_RETRIEVE) return MR_SUCCESS; id = *(int *)argv[1]; if (q->type == MR_Q_APPEND && isdigit(argv[0][0])) return MR_BAD_CHAR; EXEC SQL SELECT count(name) INTO :cnt from hostalias WHERE mach_id = :id; if (dbms_errno) return mr_errcode; /* if the type is MR_Q_APPEND, this is ahal and we need to make sure there * will be no more than 2 aliases. If it's not, it must be dhal and * any owner will do. */ if (q->type == MR_Q_APPEND && cnt >= 2) return MR_PERM; EXEC SQL SELECT m.owner_type, m.owner_id, s.owner_type, s.owner_id INTO :mtype, :mid, :stype, :sid FROM machine m, subnet s WHERE m.mach_id = :id and s.snet_id = m.snet_id; status = find_member(mtype, mid, cl); if (status) return MR_SUCCESS; status = find_member(stype, sid, cl); if (status) return MR_SUCCESS; else return MR_PERM; } /* access_snt - check for retrieving network structure */ int access_snt(struct query *q, char *argv[], client *cl) { if (q->type == MR_Q_RETRIEVE) return MR_SUCCESS; return MR_PERM; } /* access_printer */ int access_printer(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; char type[PRINTSERVERS_OWNER_TYPE_SIZE]; int id, mach_id; EXEC SQL END DECLARE SECTION; int status; mach_id = *(int *)argv[PRN_RM]; EXEC SQL SELECT owner_type, owner_id INTO :type, :id FROM printservers WHERE mach_id = :mach_id; if (sqlca.sqlcode) return MR_PERM; status = find_member(type, id, cl); if (status) return MR_SUCCESS; else return MR_PERM; } /* access_zephyr */ int access_zephyr(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; char type[ZEPHYR_OWNER_TYPE_SIZE]; char *class; int id; EXEC SQL END DECLARE SECTION; int status; class = argv[ZA_CLASS]; EXEC SQL SELECT owner_type, owner_id INTO :type, :id FROM zephyr WHERE class = :class; if (sqlca.sqlcode) return MR_PERM; status = find_member(type, id, cl); if (status) return MR_SUCCESS; else return MR_PERM; } /* access_container - check access for most container operations * * Inputs: argv[0] - cnt_id * q - query name * cl - client name * * - check if that client is a member of the access control list * - OR, if the query is add_machine_to_container or delete_machine_from_container * check if the client is a memeber of the mem_acl list * - if the query is update_container and the container is to be renamed and * it is a top-level container, only priviledged users can do it */ int access_container(struct query *q, char *argv[], client *cl) { EXEC SQL BEGIN DECLARE SECTION; int cnt_id, acl_id, memacl_id, mach_id, machine_owner_id, flag; char acl_type[CONTAINERS_ACL_TYPE_SIZE], memacl_type[CONTAINERS_ACL_TYPE_SIZE]; char name[CONTAINERS_NAME_SIZE], *newname; char machine_owner_type[MACHINE_OWNER_TYPE_SIZE]; EXEC SQL END DECLARE SECTION; int status; cnt_id = *(int *)argv[0]; /* if amcn or dmcn, container id is the second argument */ if (strcmp(q->shortname, "amcn") == 0 || strcmp(q->shortname, "dmcn") == 0) { mach_id = *(int *)argv[0]; cnt_id = *(int *)argv[1]; } EXEC SQL SELECT acl_id, acl_type, memacl_id, memacl_type, name, publicflg INTO :acl_id, :acl_type, :memacl_id, :memacl_type, :name, :flag FROM containers WHERE cnt_id = :cnt_id; if (sqlca.sqlerrd[2] != 1) return MR_INTERNAL; /* trim off the trailing spaces */ strcpy(name, strtrim(name)); /* Only dbadmin can rename containers. */ if (!strcmp(q->shortname, "ucon")) { newname = argv[1]; if (strcmp(name, newname)) return MR_PERM; } /* check for client in access control list and return success right * away if it's there. */ if (find_member(acl_type, acl_id, cl)) return MR_SUCCESS; /* If not amcn, dmcn, we lose. */ if (strcmp(q->shortname, "amcn") && strcmp(q->shortname, "dmcn")) return MR_PERM; if (find_member(memacl_type, memacl_id, cl)) return MR_SUCCESS; /* if the container is public or the query is delete, grant access if client * is on owner list */ if (flag || q->type == MR_Q_DELETE) { EXEC SQL SELECT owner_type, owner_id INTO :machine_owner_type, :machine_owner_id FROM machine WHERE mach_id = :mach_id; if (sqlca.sqlerrd[2] == 1 && strcmp("NONE", machine_owner_type) && find_member(machine_owner_type, machine_owner_id, cl)) return MR_SUCCESS; } /* Otherwise fail. */ return MR_PERM; } int check_mail_string(char *mailstring) { EXEC SQL BEGIN DECLARE SECTION; char mname[MACHINE_NAME_SIZE]; EXEC SQL END DECLARE SECTION; char *p, *host, *hostdomain; struct hostent *hp; struct mxentry *mxrecords = NULL; int index; p = strchr(mailstring, '@'); if (p) { host = strdup(++p); /* Replace .LOCAL at end of host with .MIT.EDU if needed. */ hostdomain = strrchr(host, '.'); if (hostdomain && !strcasecmp(hostdomain, ".LOCAL")) { index = hostdomain - host; host[index] = '\0'; host = realloc(host, strlen(host) + strlen(".MIT.EDU") + 1); strcat(host, ".MIT.EDU"); } hp = gethostbyname(host); if (hp) { host = realloc(host, strlen(hp->h_name) + 1); if (host) strcpy(host, hp->h_name); } else { /* Possibly a host with no A record but MX records. Check. */ mxrecords = getmxrecords(host); if (!mxrecords) return MR_BAD_MAIL_STRING; else return MR_SUCCESS; } if (!strcasecmp(host, "MIT.EDU")) { free(host); return MR_BAD_MAIL_STRING; } EXEC SQL DECLARE csr_listmem CURSOR FOR SELECT UNIQUE m.name FROM machine m, serverhosts sh WHERE m.mach_id = sh.mach_id AND (sh.service = 'MAILHUB' or sh.service = 'POSTOFFICE'); if (dbms_errno) { free(host); return mr_errcode; } EXEC SQL OPEN csr_listmem; if (dbms_errno) { free(host); return mr_errcode; } while (1) { EXEC SQL FETCH csr_listmem INTO :mname; if (sqlca.sqlcode) break; if (!strcasecmp(host, strtrim(mname))) { free(host); return MR_BAD_MAIL_STRING; } } free(host); } return MR_SUCCESS; } /* * This ought to be in the bind library. It's adapted from sendmail. */ /* * These are defined in RFC833. Some bind interface headers don't declare them. * Ghod help us if they're ever actually incompatible with what's in * the arpa/nameser.h header. */ #ifndef PACKETSZ #define PACKETSZ 512 /* maximum packet size */ #endif #ifndef HFIXEDSZ #define HFIXEDSZ 12 /* #/bytes of fixed data in header */ #endif #ifndef INT32SZ #define INT32SZ 4 /* for systems without 32-bit ints */ #endif #ifndef INT16SZ #define INT16SZ 2 /* for systems without 16-bit ints */ #endif /* minimum possible size of MX record in packet */ #define MIN_MX_SIZE 8 /* corresp to "a.com 0" w/ terminating space */ struct mxentry *getmxrecords(const char *name) { char answer[PACKETSZ], *eom, *cp, *bp; int n, ancount, qdcount, buflen, type, pref, ind; static struct mxentry pmx[(PACKETSZ - HFIXEDSZ) / MIN_MX_SIZE]; static char MXHostBuf[PACKETSZ - HFIXEDSZ]; HEADER *hp; pmx->name = (char *)NULL; pmx->pref = -1; n = res_search(name, C_IN,T_MX, (unsigned char *)&answer, sizeof(answer)); if (n == -1) return((struct mxentry *)NULL); if ((size_t)n > sizeof(answer)) n = sizeof(answer); hp = (HEADER *)&answer; cp = answer + HFIXEDSZ; eom = answer + n; h_errno = 0; for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) if ((n = dn_skipname((unsigned char *)cp, (unsigned char *)eom)) < 0) return((struct mxentry *)NULL); buflen = sizeof(MXHostBuf) - 1; bp = MXHostBuf; ind = 0; ancount = ntohs(hp->ancount); while (--ancount >= 0 && cp < eom) { if ((n = dn_expand((unsigned char *)answer, (unsigned char *)eom, (unsigned char *)cp, bp, buflen)) < 0) break; cp += n; GETSHORT(type, cp); cp += INT16SZ + INT32SZ; GETSHORT(n, cp); if (type != T_MX) { cp += n; continue; } GETSHORT(pref, cp); if ((n = dn_expand((unsigned char *)answer, (unsigned char *)eom, (unsigned char *)cp, bp, buflen)) < 0) break; cp += n; pmx[ind].name = bp; pmx[ind].pref = pref; ++ind; n = strlen((const char *)bp); bp += n; *bp++ = '\0'; buflen -= n + 1; } pmx[ind].name = (char *)NULL; pmx[ind].pref = -1; return(pmx); }