diff options
| author | Greg Hudson <ghudson@mit.edu> | 2009-10-25 16:55:12 +0000 |
|---|---|---|
| committer | Greg Hudson <ghudson@mit.edu> | 2009-10-25 16:55:12 +0000 |
| commit | 8d31a9d396f5bea88def4db395ad12dca2ac2e9f (patch) | |
| tree | 244f8f5b525432a2a2a280403f38d7b2fbdc0dfd /src/kadmin/dbutil/dump.c | |
| parent | b82e46df9b6cbf663512985a99c6d79f2b0cb796 (diff) | |
Account lockout
Merge Luke's users/lhoward/lockout2 branch to trunk. Implements
account lockout policies for preauth-using principals using existing
principal metadata fields and new policy fields. The kadmin API
version is bumped from 2 to 3 to compatibly extend the policy_ent_rec
structure.
ticket: 6577
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23038 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/kadmin/dbutil/dump.c')
| -rw-r--r-- | src/kadmin/dbutil/dump.c | 285 |
1 files changed, 231 insertions, 54 deletions
diff --git a/src/kadmin/dbutil/dump.c b/src/kadmin/dbutil/dump.c index 8dab4605c..c03e88d12 100644 --- a/src/kadmin/dbutil/dump.c +++ b/src/kadmin/dbutil/dump.c @@ -66,13 +66,17 @@ static int recursive; #include <regexp.h> #endif /* !HAVE_REGCOMP && HAVE_REGEXP_H */ +#define FLAG_VERBOSE 0x1 /* be verbose */ +#define FLAG_UPDATE 0x2 /* processing an update */ +#define FLAG_OMIT_NRA 0x4 /* avoid dumping non-replicated attrs */ + struct dump_args { char *programname; FILE *ofile; krb5_context kcontext; char **names; int nnames; - int verbose; + int flags; }; static krb5_error_code dump_k5beta_iterator (krb5_pointer, @@ -92,6 +96,7 @@ static krb5_error_code dump_k5beta7_princ_withpolicy static krb5_error_code dump_ov_princ (krb5_pointer, krb5_db_entry *); static void dump_k5beta7_policy (void *, osa_policy_ent_t); +static void dump_r1_8_policy (void *, osa_policy_ent_t); typedef krb5_error_code (*dump_func)(krb5_pointer, krb5_db_entry *); @@ -102,6 +107,8 @@ static int process_k5beta6_record (char *, krb5_context, FILE *, int, int *); static int process_k5beta7_record (char *, krb5_context, FILE *, int, int *); +static int process_r1_8_record (char *, krb5_context, + FILE *, int, int *); static int process_ov_record (char *, krb5_context, FILE *, int, int *); typedef krb5_error_code (*load_func)(char *, krb5_context, @@ -172,6 +179,24 @@ dump_version r1_3_version = { dump_k5beta7_policy, process_k5beta7_record, }; +dump_version r1_8_version = { + "Kerberos version 5 release 1.8", + "kdb5_util load_dump version 6\n", + 0, + 0, + dump_k5beta7_princ_withpolicy, + dump_r1_8_policy, + process_r1_8_record, +}; +dump_version ipropx_1_version = { + "Kerberos iprop extensible version", + "ipropx", + 0, + 0, + dump_k5beta7_princ_withpolicy, + dump_r1_8_policy, + process_r1_8_record, +}; /* External data */ extern char *current_dbname; @@ -197,6 +222,7 @@ static const char null_mprinc_name[] = "kdb5_dump@MISSING"; #define stand_fmt_name "Kerberos version 5" #define old_fmt_name "Kerberos version 5 old format" #define b6_fmt_name "Kerberos version 5 beta 6 format" +#define r1_3_fmt_name "Kerberos version 5 release 1.3 format" #define ofopen_error "%s: cannot open %s for writing (%s)\n" #define oflock_error "%s: cannot lock %s (%s)\n" #define dumprec_err "%s: error performing %s dump (%s)\n" @@ -257,6 +283,7 @@ static const char verboseoption[] = "-verbose"; static const char updateoption[] = "-update"; static const char hashoption[] = "-hash"; static const char ovoption[] = "-ov"; +static const char r13option[] = "-r13"; static const char dump_tmptrail[] = "~"; /* @@ -646,8 +673,11 @@ dump_k5beta_iterator(ptr, entry) (krb5_int32) pkey->key_data_kvno, entry->max_life, entry->max_renewable_life, 1 /* Fake mkvno */, entry->expiration, entry->pw_expiration, - last_pwd_change, entry->last_success, entry->last_failed, - entry->fail_auth_count, mod_name, mod_date, + last_pwd_change, + (arg->flags & FLAG_OMIT_NRA) ? 0 : entry->last_success, + (arg->flags & FLAG_OMIT_NRA) ? 0 : entry->last_failed, + (arg->flags & FLAG_OMIT_NRA) ? 0 : entry->fail_auth_count, + mod_name, mod_date, entry->attributes, pkey->key_data_type[1]); /* Pound out the salt data, if present. */ @@ -670,7 +700,7 @@ dump_k5beta_iterator(ptr, entry) } fprintf(arg->ofile, ";\n"); /* If we're blabbing, do it */ - if (arg->verbose) + if (arg->flags & FLAG_VERBOSE) fprintf(stderr, "%s\n", name); free(mod_name); } @@ -790,9 +820,9 @@ dump_k5beta6_iterator_ext(ptr, entry, kadm) entry->max_renewable_life, entry->expiration, entry->pw_expiration, - entry->last_success, - entry->last_failed, - entry->fail_auth_count); + (arg->flags & FLAG_OMIT_NRA) ? 0 : entry->last_success, + (arg->flags & FLAG_OMIT_NRA) ? 0 : entry->last_failed, + (arg->flags & FLAG_OMIT_NRA) ? 0 : entry->fail_auth_count); /* Pound out tagged data. */ for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { if (tlp->tl_data_type == KRB5_TL_KADM_DATA && !kadm) @@ -839,7 +869,7 @@ dump_k5beta6_iterator_ext(ptr, entry, kadm) /* Print trailer */ fprintf(arg->ofile, ";\n"); - if (arg->verbose) + if (arg->flags & FLAG_VERBOSE) fprintf(stderr, "%s\n", name); } else { @@ -926,6 +956,19 @@ void dump_k5beta7_policy(void *data, osa_policy_ent_t entry) entry->policy_refcnt); } +void dump_r1_8_policy(void *data, osa_policy_ent_t entry) +{ + struct dump_args *arg; + + arg = (struct dump_args *) data; + fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", + entry->name, + entry->pw_min_life, entry->pw_max_life, entry->pw_min_length, + entry->pw_min_classes, entry->pw_history_num, + entry->policy_refcnt, entry->pw_max_fail, + entry->pw_failcnt_interval, entry->pw_lockout_duration); +} + static void print_key_data(FILE *f, krb5_key_data *key_data) { int c; @@ -963,7 +1006,8 @@ static void print_key_data(FILE *f, krb5_key_data *key_data) static krb5_error_code dump_ov_princ(krb5_pointer ptr, krb5_db_entry *kdb) { char *princstr; - int x, y, foundcrc; + unsigned int x; + int y, foundcrc; struct dump_args *arg; krb5_tl_data tl_data; osa_princ_ent_rec adb; @@ -986,7 +1030,7 @@ static krb5_error_code dump_ov_princ(krb5_pointer ptr, krb5_db_entry *kdb) return 0; memset(&adb, 0, sizeof(adb)); - xdrmem_create(&xdrs, tl_data.tl_data_contents, + xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents, tl_data.tl_data_length, XDR_DECODE); if (! xdr_osa_princ_ent_rec(&xdrs, &adb)) { xdr_destroy(&xdrs); @@ -1034,7 +1078,7 @@ static krb5_error_code dump_ov_princ(krb5_pointer ptr, krb5_db_entry *kdb) /* * usage is: - * dump_db [-old] [-b6] [-b7] [-ov] [-verbose] [-mkey_convert] + * dump_db [-old] [-b6] [-b7] [-ov] [-r13] [-verbose] [-mkey_convert] * [-new_mkey_file mkey_file] [-rev] [-recurse] * [filename [principals...]] */ @@ -1054,13 +1098,14 @@ dump_db(argc, argv) bool_t dump_sno = FALSE; kdb_log_context *log_ctx; char **db_args = 0; /* XXX */ + unsigned int ipropx_version = IPROPX_VERSION_0; /* * Parse the arguments. */ ofile = (char *) NULL; - dump = &r1_3_version; - arglist.verbose = 0; + dump = &r1_8_version; + arglist.flags = 0; new_mkey_file = 0; mkey_convert = 0; backwards = 0; @@ -1079,9 +1124,13 @@ dump_db(argc, argv) dump = &beta7_version; else if (!strcmp(argv[aindex], ovoption)) dump = &ov_version; - else if (!strcmp(argv[aindex], ipropoption)) { + else if (!strcmp(argv[aindex], r13option)) + dump = &r1_3_version; + else if (!strncmp(argv[aindex], ipropoption, sizeof(ipropoption) - 1)) { if (log_ctx && log_ctx->iproprole) { - dump = &iprop_version; + /* Note: ipropx_version is the maximum version acceptable */ + ipropx_version = atoi(argv[aindex] + sizeof(ipropoption) - 1); + dump = ipropx_version ? &ipropx_1_version : &iprop_version; /* * dump_sno is used to indicate if the serial * # should be populated in the output @@ -1089,13 +1138,18 @@ dump_db(argc, argv) * the slave's update log when loading */ dump_sno = TRUE; + /* + * FLAG_OMIT_NRA is set to indicate that non-replicated + * attributes should be omitted. + */ + arglist.flags |= FLAG_OMIT_NRA; } else { fprintf(stderr, _("Iprop not enabled\n")); exit_status++; return; } } else if (!strcmp(argv[aindex], verboseoption)) - arglist.verbose++; + arglist.flags |= FLAG_VERBOSE; else if (!strcmp(argv[aindex], "-mkey_convert")) mkey_convert = 1; else if (!strcmp(argv[aindex], "-new_mkey_file")) { @@ -1263,6 +1317,8 @@ dump_db(argc, argv) goto unlock_and_return; } + if (ipropx_version) + fprintf(f, " %u", IPROPX_VERSION); fprintf(f, " %u", log_ctx->ulog->kdb_last_sno); fprintf(f, " %u", log_ctx->ulog->kdb_last_time.seconds); fprintf(f, " %u", log_ctx->ulog->kdb_last_time.useconds); @@ -1467,11 +1523,11 @@ update_tl_data(kcontext, dbentp, mod_name, mod_date, last_pwd_change) * Returns -1 for end of file, 0 for success and 1 for failure. */ static int -process_k5beta_record(fname, kcontext, filep, verbose, linenop) +process_k5beta_record(fname, kcontext, filep, flags, linenop) char *fname; krb5_context kcontext; FILE *filep; - int verbose; + int flags; int *linenop; { int nmatched; @@ -1723,7 +1779,7 @@ process_k5beta_record(fname, kcontext, filep, verbose, linenop) error++; } else { - if (verbose) + if (flags & FLAG_VERBOSE) fprintf(stderr, add_princ_fmt, name); retval = 0; } @@ -1773,11 +1829,11 @@ process_k5beta_record(fname, kcontext, filep, verbose, linenop) * Returns -1 for end of file, 0 for success and 1 for failure. */ static int -process_k5beta6_record(fname, kcontext, filep, verbose, linenop) +process_k5beta6_record(fname, kcontext, filep, flags, linenop) char *fname; krb5_context kcontext; FILE *filep; - int verbose; + int flags; int *linenop; { int retval; @@ -2024,7 +2080,7 @@ process_k5beta6_record(fname, kcontext, filep, verbose, linenop) name, error_message(kret)); } else { - if (verbose) + if (flags & FLAG_VERBOSE) fprintf(stderr, add_princ_fmt, name); retval = 0; } @@ -2061,17 +2117,19 @@ process_k5beta6_record(fname, kcontext, filep, verbose, linenop) } static int -process_k5beta7_policy(fname, kcontext, filep, verbose, linenop) +process_k5beta7_policy(fname, kcontext, filep, flags, linenop) char *fname; krb5_context kcontext; FILE *filep; - int verbose; + int flags; int *linenop; { osa_policy_ent_rec rec; char namebuf[1024]; int nread, ret; + memset(&rec, 0, sizeof(rec)); + (*linenop)++; rec.name = namebuf; @@ -2095,23 +2153,73 @@ process_k5beta7_policy(fname, kcontext, filep, verbose, linenop) return 1; } } - if (verbose) + if (flags & FLAG_VERBOSE) fprintf(stderr, "created policy %s\n", rec.name); return 0; } +static int +process_r1_8_policy(fname, kcontext, filep, flags, linenop) + char *fname; + krb5_context kcontext; + FILE *filep; + int flags; + int *linenop; +{ + osa_policy_ent_rec rec; + char namebuf[1024]; + int nread, ret; + + memset(&rec, 0, sizeof(rec)); + + (*linenop)++; + rec.name = namebuf; + + /* + * To make this compatible with future policy extensions, we + * ignore any additional values. + */ + nread = fscanf(filep, "%1024s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d", + rec.name, + &rec.pw_min_life, &rec.pw_max_life, + &rec.pw_min_length, &rec.pw_min_classes, + &rec.pw_history_num, &rec.policy_refcnt, + &rec.pw_max_fail, &rec.pw_failcnt_interval, + &rec.pw_lockout_duration); + if (nread == EOF) + return -1; + else if (nread < 10) { + fprintf(stderr, "cannot parse policy on line %d (%d read)\n", + *linenop, nread); + return 1; + } + + if ((ret = krb5_db_create_policy(kcontext, &rec))) { + if (ret && + ((ret = krb5_db_put_policy(kcontext, &rec)))) { + fprintf(stderr, "cannot create policy on line %d: %s\n", + *linenop, error_message(ret)); + return 1; + } + } + if (flags & FLAG_VERBOSE) + fprintf(stderr, "created policy %s\n", rec.name); + + return 0; +} + /* * process_k5beta7_record() - Handle a dump record in krb5b7 format. * * Returns -1 for end of file, 0 for success and 1 for failure. */ static int -process_k5beta7_record(fname, kcontext, filep, verbose, linenop) +process_k5beta7_record(fname, kcontext, filep, flags, linenop) char *fname; krb5_context kcontext; FILE *filep; - int verbose; + int flags; int *linenop; { int nread; @@ -2123,10 +2231,10 @@ process_k5beta7_record(fname, kcontext, filep, verbose, linenop) else if (nread != 1) return 1; if (strcmp(rectype, "princ") == 0) - process_k5beta6_record(fname, kcontext, filep, verbose, + process_k5beta6_record(fname, kcontext, filep, flags, linenop); else if (strcmp(rectype, "policy") == 0) - process_k5beta7_policy(fname, kcontext, filep, verbose, + process_k5beta7_policy(fname, kcontext, filep, flags, linenop); else { fprintf(stderr, "unknown record type \"%s\" on line %d\n", @@ -2143,11 +2251,11 @@ process_k5beta7_record(fname, kcontext, filep, verbose, linenop) * Returns -1 for end of file, 0 for success and 1 for failure. */ static int -process_ov_record(fname, kcontext, filep, verbose, linenop) +process_ov_record(fname, kcontext, filep, flags, linenop) char *fname; krb5_context kcontext; FILE *filep; - int verbose; + int flags; int *linenop; { int nread; @@ -2159,10 +2267,10 @@ process_ov_record(fname, kcontext, filep, verbose, linenop) else if (nread != 1) return 1; if (strcmp(rectype, "princ") == 0) - process_ov_principal(fname, kcontext, filep, verbose, + process_ov_principal(fname, kcontext, filep, flags, linenop); else if (strcmp(rectype, "policy") == 0) - process_k5beta7_policy(fname, kcontext, filep, verbose, + process_k5beta7_policy(fname, kcontext, filep, flags, linenop); else if (strcmp(rectype, "End") == 0) return -1; @@ -2176,15 +2284,51 @@ process_ov_record(fname, kcontext, filep, verbose, linenop) } /* + * process_r1_8_record() - Handle a dump record in krb5 1.8 format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_r1_8_record(fname, kcontext, filep, flags, linenop) + char *fname; + krb5_context kcontext; + FILE *filep; + int flags; + int *linenop; +{ + int nread; + char rectype[100]; + + nread = fscanf(filep, "%100s\t", rectype); + if (nread == EOF) + return -1; + else if (nread != 1) + return 1; + if (strcmp(rectype, "princ") == 0) + process_k5beta6_record(fname, kcontext, filep, flags, + linenop); + else if (strcmp(rectype, "policy") == 0) + process_r1_8_policy(fname, kcontext, filep, flags, + linenop); + else { + fprintf(stderr, "unknown record type \"%s\" on line %d\n", + rectype, *linenop); + return 1; + } + + return 0; +} + +/* * restore_dump() - Restore the database from any version dump file. */ static int -restore_dump(programname, kcontext, dumpfile, f, verbose, dump) +restore_dump(programname, kcontext, dumpfile, f, flags, dump) char *programname; krb5_context kcontext; char *dumpfile; FILE *f; - int verbose; + int flags; dump_version *dump; { int error; @@ -2199,7 +2343,7 @@ restore_dump(programname, kcontext, dumpfile, f, verbose, dump) while (!(error = (*dump->load_record)(dumpfile, kcontext, f, - verbose, + flags, &lineno))) ; if (error != -1) @@ -2211,8 +2355,8 @@ restore_dump(programname, kcontext, dumpfile, f, verbose, dump) } /* - * Usage: load_db [-old] [-ov] [-b6] [-b7] [-verbose] [-update] [-hash] - * filename + * Usage: load_db [-old] [-ov] [-b6] [-b7] [-r13] [-verbose] + * [-update] [-hash] filename */ void load_db(argc, argv) @@ -2230,13 +2374,13 @@ load_db(argc, argv) char *dbname_tmp; char buf[BUFSIZ]; dump_version *load; - int update, verbose; + int flags; krb5_int32 crflags; int aindex; int db_locked = 0; char iheader[MAX_HEADER]; kdb_log_context *log_ctx; - int add_update = 1; + krb5_boolean add_update = TRUE; uint32_t caller, last_sno, last_seconds, last_useconds; /* @@ -2245,8 +2389,7 @@ load_db(argc, argv) dumpfile = (char *) NULL; dbname = global_params.dbname; load = NULL; - update = 0; - verbose = 0; + flags = 0; crflags = KRB5_KDB_CREATE_BTREE; exit_status = 0; dbname_tmp = (char *) NULL; @@ -2261,6 +2404,8 @@ load_db(argc, argv) load = &beta7_version; else if (!strcmp(argv[aindex], ovoption)) load = &ov_version; + else if (!strcmp(argv[aindex], r13option)) + load = &r1_3_version; else if (!strcmp(argv[aindex], ipropoption)) { if (log_ctx && log_ctx->iproprole) { load = &iprop_version; @@ -2271,9 +2416,9 @@ load_db(argc, argv) return; } } else if (!strcmp(argv[aindex], verboseoption)) - verbose = 1; + flags |= FLAG_VERBOSE; else if (!strcmp(argv[aindex], updateoption)) - update = 1; + flags |= FLAG_UPDATE; else if (!strcmp(argv[aindex], hashoption)) { if (!add_db_arg("hash=true")) { com_err(progname, ENOMEM, "while parsing command arguments\n"); @@ -2342,6 +2487,7 @@ load_db(argc, argv) fgets(buf, sizeof(buf), f); if (load) { /* only check what we know; some headers only contain a prefix */ + /* NB: this should work for ipropx even though load is iprop */ if (strncmp(buf, load->header, strlen(load->header)) != 0) { fprintf(stderr, head_bad_fmt, progname, dumpfile); exit_status++; @@ -2358,6 +2504,8 @@ load_db(argc, argv) load = &beta7_version; else if (strcmp(buf, r1_3_version.header) == 0) load = &r1_3_version; + else if (strcmp(buf, r1_8_version.header) == 0) + load = &r1_8_version; else if (strncmp(buf, ov_version.header, strlen(ov_version.header)) == 0) load = &ov_version; @@ -2368,7 +2516,7 @@ load_db(argc, argv) return; } } - if (load->updateonly && !update) { + if (load->updateonly && !(flags & FLAG_UPDATE)) { fprintf(stderr, "%s: dump version %s can only be loaded with the " "-update flag\n", progname, load->name); exit_status++; @@ -2381,7 +2529,7 @@ load_db(argc, argv) * be the live db. */ newparams = global_params; - if (! update) { + if (! (flags & FLAG_UPDATE)) { newparams.mask |= KADM5_CONFIG_DBNAME; newparams.dbname = dbname_tmp; @@ -2397,12 +2545,17 @@ load_db(argc, argv) com_err(progname, ENOMEM, "computing parameters for database"); exit(1); } + + if (!add_update && !add_db_arg("merge_nra")) { + com_err(progname, ENOMEM, "computing parameters for database"); + exit(1); + } } /* * If not an update restoration, create the database. otherwise open */ - if (!update) { + if (!(flags & FLAG_UPDATE)) { if((kret = krb5_db_create(kcontext, db5util_db_args))) { const char *emsg = krb5_get_error_message(kcontext, kret); /* @@ -2452,7 +2605,10 @@ load_db(argc, argv) * If an update restoration, make sure the db is left unusable if * the update fails. */ - if ((kret = krb5_db_lock(kcontext, update?KRB5_DB_LOCKMODE_PERMANENT: KRB5_DB_LOCKMODE_EXCLUSIVE))) { + if ((kret = krb5_db_lock(kcontext, + (flags & FLAG_UPDATE) ? + KRB5_DB_LOCKMODE_PERMANENT : + KRB5_DB_LOCKMODE_EXCLUSIVE))) { /* * Ignore a not supported error since there is nothing to do about it * anyway. @@ -2491,7 +2647,7 @@ load_db(argc, argv) * we could implicity delete db entries during a replace * no advantage in incr updates when entire db is replaced */ - if (!update) { + if (!(flags & FLAG_UPDATE)) { memset(log_ctx->ulog, 0, sizeof (kdb_hlog_t)); log_ctx->ulog->kdb_hmagic = KDB_ULOG_HDR_MAGIC; @@ -2502,9 +2658,30 @@ load_db(argc, argv) log_ctx->iproprole = IPROP_NULL; if (!add_update) { - sscanf(buf, "%s %u %u %u", iheader, &last_sno, + unsigned int ipropx_version = IPROPX_VERSION_0; + + if (!strncmp(buf, "ipropx ", sizeof("ipropx ") - 1)) + sscanf(buf, "%s %u %u %u %u", iheader, + &ipropx_version, &last_sno, + &last_seconds, &last_useconds); + else + sscanf(buf, "%s %u %u %u", iheader, &last_sno, &last_seconds, &last_useconds); + switch (ipropx_version) { + case IPROPX_VERSION_0: + load = &iprop_version; + break; + case IPROPX_VERSION_1: + load = &ipropx_1_version; + break; + default: + fprintf(stderr, _("%s: Unknown iprop dump version %d\n"), + progname, ipropx_version); + exit_status++; + goto error; + } + log_ctx->ulog->kdb_last_sno = last_sno; log_ctx->ulog->kdb_last_time.seconds = last_seconds; @@ -2515,13 +2692,13 @@ load_db(argc, argv) } if (restore_dump(progname, kcontext, (dumpfile) ? dumpfile : stdin_name, - f, verbose, load)) { + f, flags, load)) { fprintf(stderr, restfail_fmt, progname, load->name); exit_status++; } - if (!update && load->create_kadm5 && + if (!(flags & FLAG_UPDATE) && load->create_kadm5 && ((kret = kadm5_create_magic_princs(&newparams, kcontext)))) { /* error message printed by create_magic_princs */ exit_status++; @@ -2544,7 +2721,7 @@ load_db(argc, argv) /* close policy db below */ - if (exit_status == 0 && !update) { + if (exit_status == 0 && !(flags & FLAG_UPDATE)) { kret = krb5_db_promote(kcontext, db5util_db_args); /* * Ignore a not supported error since there is nothing to do about it @@ -2564,7 +2741,7 @@ error: * * If an update: if there was no error, unlock the database. */ - if (!update) { + if (!(flags & FLAG_UPDATE)) { if (exit_status) { kret = krb5_db_destroy(kcontext, db5util_db_args); /* |
