From 7c754902e9b3c5cfeca35e77c86f6c172b3dc56b Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Nov 30 2022 18:22:07 +0000 Subject: Switch to CA user when saving NSS certificates A new parameter was added, nss-user, which indicates the user to become via setuid/setgid when saving certificates in NSS. This is necessary when using SoftHSM as a PKCS#11 device to keep the filesystem permissions correct. Also tweak cleaning up duplicate certificates. certmonger makes an effort to remove any duplicates, those with duplicated nicknames with different certs, etc. It didn't handle those with tokens though in NSS. A certificate in a token will have a mirrored entry in the database to store trust information. This was being seen as a "duplicate" and certmogner was removing it, thus removing the trust. Fixes: https://pagure.io/certmonger/issue/243 Signed-off-by: Rob Crittenden --- diff --git a/src/certsave-n.c b/src/certsave-n.c index cb47072..5ddf7ad 100644 --- a/src/certsave-n.c +++ b/src/certsave-n.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -83,6 +85,20 @@ add_privkey_to_list(SECKEYPrivateKey **list, SECKEYPrivateKey *key) return list; } +/* Return a nickname minus the token */ +static char * +cm_get_nickname(char *data) +{ + char *p = NULL; + + if (strchr(data, ':') != NULL) { + p = strrchr(data, ':') + 1; + } else { + p = data; + } + return p; +} + static int cm_certsave_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, void *userdata) @@ -123,6 +139,62 @@ cm_certsave_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, } /* Open the database. */ + if (entry->cm_nss_user != NULL) { + struct passwd *pwd; + struct group *grp; + char *user, *group = NULL; + uid_t uid; + gid_t gid; + + user = strdup(entry->cm_nss_user); + group = strchr(user, ':'); + if (group != NULL) { + *group++ = '\0'; + if (strlen(group) == 0) { + group = NULL; + } + } + + errno = 0; + pwd = getpwnam(user); + if (pwd == NULL) { + cm_log(0, "Error looking up user \"%s\", " + "not setting identity: %s.\n", + user, strerror(errno)); + free(user); + _exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR); + } + uid = pwd->pw_uid; + gid = pwd->pw_gid; + if (group != NULL) { + grp = getgrnam(group); + if (grp == NULL) { + cm_log(0, "Error looking up group \"%s\", " + "not setting identity.\n", + group); + free(user); + _exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR); + } + gid = grp->gr_gid; + } + free(user); + + cm_log(1, "Switching to %s %d:%d\n", pwd->pw_name, uid, gid); + + if (initgroups(pwd->pw_name, gid) == -1) { + cm_log(0, "initgroups error (%s: %d): %s\n", pwd->pw_name, gid, strerror(errno)); + _exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR); + } + if (setgid(gid) == -1) { + cm_log(0, "setgid error (%d): %s\n", gid, strerror(errno)); + _exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR); + } + if (setuid(uid) == -1) { + cm_log(0, "setuid error (%d): %s\n", uid, strerror(errno)); + _exit(CM_CERTSAVE_STATUS_INTERNAL_ERROR); + } + } + settings = userdata; readwrite = settings->readwrite; errno = 0; @@ -407,7 +479,7 @@ cm_certsave_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, if ((!SECITEM_ItemsAreEqual(&subject, &node->cert->derSubject)) && (sle->slot == node->cert->slot)) { - cm_log(3, "Found a " + cm_log(3, "1 Found a " "certificate " "with the same " "nickname but " @@ -454,22 +526,27 @@ cm_certsave_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, !CERT_LIST_END(node, certlist); node = CERT_LIST_NEXT(node)) { if ((node->cert->nickname != NULL) && - (strcmp(entry->cm_cert_nickname, - node->cert->nickname) != 0) && + (strcmp(cm_get_nickname(entry->cm_cert_nickname), + cm_get_nickname(node->cert->nickname)) != 0) && (sle->slot == node->cert->slot)) { i++; - cm_log(3, "Found a " + cm_log(3, "2 Found a " "certificate with a " "different nickname but " "the same subject, " "removing certificate " - "\"%s\" with subject " - "\"%s\".\n", + "\"%s\" vs \"%s\" with subject " + "\"%s\" in slot \"%s\" vs " + "\"%s\".\n", node->cert->nickname, + entry->cm_cert_nickname, node->cert->subjectName ? node->cert->subjectName : - ""); + "", + PK11_GetTokenName(sle->slot), + PK11_GetTokenName(node->cert->slot) + ); /* Get a handle for this * certificate's private key, * in case we need to remove @@ -573,8 +650,9 @@ cm_certsave_n_main(int fd, struct cm_store_ca *ca, struct cm_store_entry *entry, !CERT_LIST_END(node, certlist); node = CERT_LIST_NEXT(node)) { if (!SECITEM_ItemsAreEqual(item, - &node->cert->derCert)) { - cm_log(3, "Found a " + &node->cert->derCert) && + (sle->slot == node->cert->slot)) { + cm_log(3, "3 Found a " "certificate " "with the same " "nickname and " diff --git a/src/getcert.c b/src/getcert.c index ddcb739..9d82fc4 100644 --- a/src/getcert.c +++ b/src/getcert.c @@ -754,8 +754,9 @@ request(const char *argv0, int argc, const char **argv) char **principal = NULL, **dns = NULL, **email = NULL, **ipaddr = NULL; char *key_owner = NULL, *key_perms = NULL; char *cert_owner = NULL, *cert_perms = NULL; - struct cm_tdbusm_dict param[51]; - const struct cm_tdbusm_dict *params[50]; + char *nss_user = NULL; + struct cm_tdbusm_dict param[52]; + const struct cm_tdbusm_dict *params[51]; DBusMessage *req, *rep; int waitreq = 0, timeout = -1; int is_ca = 0, path_length = -1; @@ -779,6 +780,7 @@ request(const char *argv0, int argc, const char **argv) {"key-perms", 'm', POPT_ARG_STRING, NULL, 'm', _("file permissions for private key"), HELP_TYPE_MODE}, {"cert-owner", 'O', POPT_ARG_STRING, NULL, 'O', _("owner information for certificate"), HELP_TYPE_USER}, {"cert-perms", 'M', POPT_ARG_STRING, NULL, 'M', _("file permissions for certificate"), HELP_TYPE_MODE}, + {"nss-user", 'Z', POPT_ARG_STRING, NULL, 'Z', _("user to save NSS private and public keys as"), HELP_TYPE_USER}, {"ca-dbdir", 'a', POPT_ARG_STRING, NULL, 'a', _("NSS database in which to store the CA's certificates"), HELP_TYPE_DIRECTORY}, {"ca-file", 'F', POPT_ARG_STRING, NULL, 'F', _("file in which to store the CA's certificates"), HELP_TYPE_FILENAME}, {"before-command", 'B', POPT_ARG_STRING, NULL, 'B', _("command to run before saving the certificate"), HELP_TYPE_COMMAND}, @@ -929,6 +931,9 @@ request(const char *argv0, int argc, const char **argv) case 'X': issuer = talloc_strdup(globals.tctx, poptarg); break; + case 'Z': + nss_user = talloc_strdup(globals.tctx, poptarg); + break; case 'N': subject = talloc_strdup(globals.tctx, poptarg); break; @@ -1276,6 +1281,13 @@ request(const char *argv0, int argc, const char **argv) params[i] = ¶m[i]; i++; } + if (nss_user != NULL) { + param[i].key = CM_DBUS_PROP_NSS_USER; + param[i].value_type = cm_tdbusm_dict_s; + param[i].value.s = nss_user; + params[i] = ¶m[i]; + i++; + } if (keytype != NULL) { param[i].key = "KEY_TYPE"; param[i].value_type = cm_tdbusm_dict_s; @@ -1572,6 +1584,7 @@ add_basic_request(enum cm_tdbus_type bus, char *id, char *keyfile, char *certfile, char *key_owner, char *cert_owner, char *key_perms, char *cert_perms, + char *nss_user, char *pin, char *pinfile, char *cpass, char *cpassfile, char *ca, char *profile, char *issuer, @@ -1585,8 +1598,8 @@ add_basic_request(enum cm_tdbus_type bus, char *id, { DBusMessage *req, *rep; int i; - struct cm_tdbusm_dict param[31]; - const struct cm_tdbusm_dict *params[31]; + struct cm_tdbusm_dict param[32]; + const struct cm_tdbusm_dict *params[32]; dbus_bool_t b; const char *capath; char *p; @@ -1643,6 +1656,13 @@ add_basic_request(enum cm_tdbus_type bus, char *id, params[i] = ¶m[i]; i++; } + if (nss_user != NULL) { + param[i].key = CM_DBUS_PROP_NSS_USER; + param[i].value_type = cm_tdbusm_dict_s; + param[i].value.s = nss_user; + params[i] = ¶m[i]; + i++; + } } else if (certfile != NULL) { if (keyfile != NULL) { @@ -1848,8 +1868,8 @@ set_tracking(const char *argv0, const char *category, enum cm_tdbus_type bus = CM_DBUS_DEFAULT_BUS; DBusMessage *req, *rep; const char *request, *capath; - struct cm_tdbusm_dict param[28]; - const struct cm_tdbusm_dict *params[29]; + struct cm_tdbusm_dict param[29]; + const struct cm_tdbusm_dict *params[30]; char *nss_scheme, *dbdir = NULL, *token = NULL, *nickname = NULL; char **anchor_dbs = NULL, **anchor_files = NULL; char *id = NULL, *new_id = NULL, *new_request; @@ -1858,6 +1878,7 @@ set_tracking(const char *argv0, const char *category, char *ms_template_spec = NULL; char *pin = NULL, *pinfile = NULL, *cpass = NULL, *cpassfile = NULL; char *key_owner = NULL, *key_perms = NULL; + char *nss_user = NULL; char *cert_owner = NULL, *cert_perms = NULL; dbus_bool_t b; char *p; @@ -1885,6 +1906,7 @@ set_tracking(const char *argv0, const char *category, {"key-perms", 'm', POPT_ARG_STRING, NULL, 'm', _("file permissions for private key"), HELP_TYPE_MODE}, {"cert-owner", 'O', POPT_ARG_STRING, NULL, 'O', _("owner information for certificate"), HELP_TYPE_USER}, {"cert-perms", 'M', POPT_ARG_STRING, NULL, 'M', _("file permissions for certificate"), HELP_TYPE_MODE}, + {"nss-user", 'Z', POPT_ARG_STRING, NULL, 'Z', _("user to save NSS private and public keys as"), HELP_TYPE_USER}, {"ca-dbdir", 'a', POPT_ARG_STRING, NULL, 'a', _("NSS database in which to store the CA's certificates"), HELP_TYPE_DIRECTORY}, {"ca-file", 'F', POPT_ARG_STRING, NULL, 'F', _("file in which to store the CA's certificates"), HELP_TYPE_FILENAME}, {"before-command", 'B', POPT_ARG_STRING, NULL, 'B', _("command to run before saving the certificate"), HELP_TYPE_COMMAND}, @@ -2009,6 +2031,9 @@ set_tracking(const char *argv0, const char *category, case 'X': issuer = talloc_strdup(globals.tctx, poptarg); break; + case 'Z': + nss_user = talloc_strdup(globals.tctx, poptarg); + break; case 'i': id = talloc_strdup(globals.tctx, poptarg); break; @@ -2289,6 +2314,13 @@ set_tracking(const char *argv0, const char *category, params[i] = ¶m[i]; i++; } + if (nss_user != NULL) { + param[i].key = CM_DBUS_PROP_NSS_USER; + param[i].value_type = cm_tdbusm_dict_s; + param[i].value.s = nss_user; + params[i] = ¶m[i]; + i++; + } if (pin != NULL) { param[i].key = "KEY_PIN"; param[i].value_type = cm_tdbusm_dict_s; @@ -2441,6 +2473,7 @@ set_tracking(const char *argv0, const char *category, keyfile, certfile, key_owner, cert_owner, key_perms, cert_perms, + nss_user, pin, pinfile, cpass, cpassfile, ca, profile, issuer, @@ -2513,8 +2546,8 @@ rekey_or_resubmit(const char *argv0, const char *category, int argc, DBusMessage *req, *rep; const char *request; char *capath; - struct cm_tdbusm_dict param[31]; - const struct cm_tdbusm_dict *params[32]; + struct cm_tdbusm_dict param[32]; + const struct cm_tdbusm_dict *params[33]; char *dbdir = NULL, *token = NULL, *nickname = NULL, *certfile = NULL; char **anchor_dbs = NULL, **anchor_files = NULL; char *pin = NULL, *pinfile = NULL, *cpass = NULL, *cpassfile = NULL; @@ -2525,6 +2558,7 @@ rekey_or_resubmit(const char *argv0, const char *category, int argc, char *ms_template_spec = NULL; char *key_owner = NULL, *key_perms = NULL; char *cert_owner = NULL, *cert_perms = NULL; + char *nss_user = NULL; char *keytype = NULL; int keysize = 0; dbus_bool_t b; @@ -2552,6 +2586,7 @@ rekey_or_resubmit(const char *argv0, const char *category, int argc, {"key-perms", 'm', POPT_ARG_STRING, NULL, 'm', _("file permissions for private key"), HELP_TYPE_MODE}, {"cert-owner", 'O', POPT_ARG_STRING, NULL, 'O', _("owner information for certificate"), HELP_TYPE_USER}, {"cert-perms", 'M', POPT_ARG_STRING, NULL, 'M', _("file permissions for certificate"), HELP_TYPE_MODE}, + {"nss-user", 'Z', POPT_ARG_STRING, NULL, 'Z', _("user to save NSS private and public keys as"), HELP_TYPE_USER}, {"ca-dbdir", 'a', POPT_ARG_STRING, NULL, 'a', _("NSS database in which to store the CA's certificates"), HELP_TYPE_DIRECTORY}, {"ca-file", 'F', POPT_ARG_STRING, NULL, 'F', _("file in which to store the CA's certificates"), HELP_TYPE_FILENAME}, {"before-command", 'B', POPT_ARG_STRING, NULL, 'B', _("command to run before saving the certificate"), HELP_TYPE_COMMAND}, @@ -2645,6 +2680,9 @@ rekey_or_resubmit(const char *argv0, const char *category, int argc, case 'X': issuer = talloc_strdup(globals.tctx, poptarg); break; + case 'Z': + nss_user = talloc_strdup(globals.tctx, poptarg); + break; case 'i': id = talloc_strdup(globals.tctx, poptarg); break; @@ -2885,6 +2923,13 @@ rekey_or_resubmit(const char *argv0, const char *category, int argc, params[i] = ¶m[i]; i++; } + if (nss_user != NULL) { + param[i].key = CM_DBUS_PROP_NSS_USER; + param[i].value_type = cm_tdbusm_dict_s; + param[i].value.s = nss_user; + params[i] = ¶m[i]; + i++; + } if (keytype != NULL) { param[i].key = "KEY_TYPE"; param[i].value_type = cm_tdbusm_dict_s; @@ -5008,6 +5053,8 @@ help(const char *twopartcmd, const char *category) N_(" owner information for certificate\n"), N_(" -M MODE, --cert-perms=MODE\n"), N_(" file permissions for certificate\n"), + N_(" -Z USER, --nss-user=USER\n"), + N_(" User to switch to during NSS save operations\n"), NULL, }; const char *start_tracking_help[] = { @@ -5096,6 +5143,8 @@ help(const char *twopartcmd, const char *category) N_(" owner information for certificate\n"), N_(" -M MODE, --cert-perms=MODE\n"), N_(" file permissions for certificate\n"), + N_(" -Z USER, --nss-user=USER\n"), + N_(" User to switch to during NSS save operations\n"), NULL, }; const char *stop_tracking_help[] = { @@ -5204,6 +5253,8 @@ help(const char *twopartcmd, const char *category) N_(" owner information for certificate\n"), N_(" -M MODE, --cert-perms=MODE\n"), N_(" file permissions for certificate\n"), + N_(" -Z USER, --nss-user=USER\n"), + N_(" User to switch to during NSS save operations\n"), NULL, }; const char *rekey_help[] = { diff --git a/src/store-files.c b/src/store-files.c index 85ac692..848de41 100644 --- a/src/store-files.c +++ b/src/store-files.c @@ -101,6 +101,8 @@ enum cm_store_file_field { cm_store_entry_field_cert_owner, cm_store_entry_field_cert_perms, + cm_store_entry_field_nss_user, + cm_store_entry_field_cert_issuer_der, cm_store_entry_field_cert_issuer, cm_store_entry_field_cert_serial, @@ -276,6 +278,8 @@ static struct cm_store_file_field_list { {cm_store_entry_field_cert_owner, "cert_owner"}, {cm_store_entry_field_cert_perms, "cert_perms"}, + {cm_store_entry_field_nss_user, "nss_user"}, + {cm_store_entry_field_cert_issuer_der, "cert_issuer_der"}, {cm_store_entry_field_cert_issuer, "cert_issuer"}, {cm_store_entry_field_cert_serial, "cert_serial"}, @@ -1022,6 +1026,9 @@ cm_store_entry_read(void *parent, const char *filename, FILE *fp) ret->cm_key_issued_count = atoi(p); talloc_free(p); break; + case cm_store_entry_field_nss_user: + ret->cm_nss_user = free_if_empty(p); + break; case cm_store_entry_field_cert_storage_type: if (strcasecmp(p, "FILE") == 0) { ret->cm_cert_storage_type = @@ -1420,6 +1427,7 @@ cm_store_ca_read(void *parent, const char *filename, FILE *fp) case cm_store_entry_field_key_requested_count: case cm_store_entry_field_key_next_requested_count: case cm_store_entry_field_key_issued_count: + case cm_store_entry_field_nss_user: case cm_store_entry_field_cert_storage_type: case cm_store_entry_field_cert_storage_location: case cm_store_entry_field_cert_token: @@ -2042,6 +2050,9 @@ cm_store_entry_write(FILE *fp, struct cm_store_entry *entry) cm_store_file_write_int(fp, cm_store_entry_field_cert_no_ocsp_check, entry->cm_cert_no_ocsp_check ? 1 : 0); + cm_store_file_write_str(fp, cm_store_entry_field_nss_user, + entry->cm_nss_user); + cm_store_file_write_str(fp, cm_store_entry_field_last_need_notify_check, cm_store_timestamp_from_time(entry->cm_last_need_notify_check, timestamp)); @@ -2280,6 +2291,7 @@ cm_store_entry_save(struct cm_store_entry *entry) if (cm_store_entry_write(fp, entry) == 0) { fclose(fp); dest = (const char *) entry->cm_store_private; + cm_log(0, "Wrote to %s\n", dest); if (rename(path, dest) != 0) { cm_log(0, "Error renaming \"%s\" to \"%s\": " "%s.\n", path, dest, strerror(errno)); @@ -2797,6 +2809,8 @@ cm_store_entry_dup(void *parent, struct cm_store_entry *entry) ret->cm_key_next_requested_count = entry->cm_key_next_requested_count; ret->cm_key_issued_count = entry->cm_key_issued_count; + ret->cm_nss_user = cm_store_maybe_strdup(ret, entry->cm_nss_user); + ret->cm_cert_storage_type = entry->cm_cert_storage_type; ret->cm_cert_storage_location = cm_store_maybe_strdup(ret, entry->cm_cert_storage_location); ret->cm_cert_token = cm_store_maybe_strdup(ret, entry->cm_cert_token); diff --git a/src/store-int.h b/src/store-int.h index 4a40406..7557919 100644 --- a/src/store-int.h +++ b/src/store-int.h @@ -61,6 +61,7 @@ struct cm_store_entry { char *cm_key_pin_file; char *cm_key_owner; mode_t cm_key_perms; + char *cm_nss_user; /* Cached plain public key (used for computing subject and authority key IDs) */ char *cm_key_pubkey, *cm_key_next_pubkey; /* Cached public key info (used in signing requests when using NSS) */ diff --git a/src/tdbus.h b/src/tdbus.h index e63e783..1176bcf 100644 --- a/src/tdbus.h +++ b/src/tdbus.h @@ -81,6 +81,7 @@ #define CM_DBUS_PROP_KEY_PERMS "key-perms" #define CM_DBUS_PROP_KEY_TYPE "key-type" #define CM_DBUS_PROP_KEY_SIZE "key-size" +#define CM_DBUS_PROP_NSS_USER "nss-user" #define CM_DBUS_PROP_MONITORING "monitoring" #define CM_DBUS_PROP_NOTIFICATION_TYPE "notification-type" #define CM_DBUS_PROP_NOTIFICATION_SYSLOG_PRIORITY "notification-syslog-priority" diff --git a/src/tdbush.c b/src/tdbush.c index b573922..29475fb 100644 --- a/src/tdbush.c +++ b/src/tdbush.c @@ -412,6 +412,7 @@ base_add_request(DBusConnection *conn, DBusMessage *msg, enum cm_cert_storage_type cert_storage; char *cert_location, *cert_nickname, *cert_token; char *cert_owner, *key_owner; + char *nss_user; mode_t cert_perms, key_perms; char *path, *pre_command, *post_command; char **root_cert_nssdbs, **root_cert_files; @@ -1265,6 +1266,15 @@ base_add_request(DBusConnection *conn, DBusMessage *msg, } else { key_perms = 0; } + param = cm_tdbusm_find_dict_entry(d, + CM_DBUS_PROP_NSS_USER, + cm_tdbusm_dict_s); + if (param != NULL) { + nss_user = param->value.s; + cm_log(1, "Setting CM_DBUS_PROP_NSS_USER to %s\n", nss_user); + } else { + nss_user = NULL; + } /* Okay, we can go ahead and add the entry. */ new_entry = cm_store_entry_new(parent); if (new_entry == NULL) { @@ -1374,6 +1384,7 @@ base_add_request(DBusConnection *conn, DBusMessage *msg, new_entry->cm_cert_token = maybe_strdup(new_entry, cert_token); new_entry->cm_cert_owner = maybe_strdup(new_entry, cert_owner); new_entry->cm_cert_perms = cert_perms; + new_entry->cm_nss_user = maybe_strdup(new_entry, nss_user); new_entry->cm_root_cert_store_nssdbs = maybe_strdupv(new_entry, root_cert_nssdbs); new_entry->cm_root_cert_store_files = maybe_strdupv(new_entry, root_cert_files); @@ -3290,6 +3301,14 @@ request_modify(DBusConnection *conn, DBusMessage *msg, propname[n_propname++] = CM_DBUS_PROP_KEY_PERMS; } } else + if ((param->value_type == cm_tdbusm_dict_s) && + (strcasecmp(param->key, CM_DBUS_PROP_NSS_USER) == 0)) { + entry->cm_nss_user = talloc_strdup(entry, param->value.s); + cm_log(1, "Param CM_DBUS_PROP_NSS_USER to %s\n", entry->cm_nss_user); + if (n_propname + 2 < sizeof(propname) / sizeof(propname[0])) { + propname[n_propname++] = CM_DBUS_PROP_NSS_USER; + } + } else if ((param->value_type == cm_tdbusm_dict_b) && ((strcasecmp(param->key, "RENEW") == 0) || (strcasecmp(param->key, CM_DBUS_PROP_AUTORENEW) == 0))) { @@ -7218,6 +7237,15 @@ cm_tdbush_iface_request(void) NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), make_interface_item(cm_tdbush_interface_property, + make_property(CM_DBUS_PROP_NSS_USER, + cm_tdbush_property_string, + cm_tdbush_property_readwrite, + cm_tdbush_property_char_p, + offsetof(struct cm_store_entry, cm_nss_user), + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL), + make_interface_item(cm_tdbush_interface_property, make_property(CM_DBUS_PROP_ROOT_CERT_FILES, cm_tdbush_property_strings, cm_tdbush_property_read, @@ -7396,7 +7424,7 @@ cm_tdbush_iface_request(void) make_interface_item(cm_tdbush_interface_signal, make_signal(CM_DBUS_SIGNAL_REQUEST_CERT_SAVED, NULL), - NULL))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))); + NULL)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))); } return ret; } diff --git a/tests/028-dbus/expected.out b/tests/028-dbus/expected.out index 4cecbe1..86cba02 100644 --- a/tests/028-dbus/expected.out +++ b/tests/028-dbus/expected.out @@ -380,6 +380,7 @@ OK + diff --git a/tests/028-dbus/expected.out.nodsa b/tests/028-dbus/expected.out.nodsa index 0e1b977..b92b719 100644 --- a/tests/028-dbus/expected.out.nodsa +++ b/tests/028-dbus/expected.out.nodsa @@ -380,6 +380,7 @@ OK +