summaryrefslogtreecommitdiffstats
path: root/daemons
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2013-09-17 00:30:14 -0400
committerMartin Kosek <mkosek@redhat.com>2014-06-26 10:30:53 +0200
commit5c0e7a5fb420377dcc06a956695afdcb35196444 (patch)
tree20458627de698dcb7dbc2b06c00ab9ea155c5b99 /daemons
parent88bcf5899c3bd12b05d017436df0fc1374c954a5 (diff)
downloadfreeipa-5c0e7a5fb420377dcc06a956695afdcb35196444.tar.gz
freeipa-5c0e7a5fb420377dcc06a956695afdcb35196444.tar.xz
freeipa-5c0e7a5fb420377dcc06a956695afdcb35196444.zip
keytab: Add new extended operation to get a keytab.
This new extended operation allow to create new keys or retrieve existing ones. The new set of keys is returned as a ASN.1 structure similar to the one that is passed in by the 'set keytab' extended operation. Access to the operation is regulated through a new special ACI that allows 'retrieval' only if the user has access to an attribute named ipaProtectedOperation postfixed by the subtypes 'read_keys' and 'write_keys' to distinguish between creation and retrieval operation. For example for allowing retrieval by a specific user the following ACI is set on cn=accounts: (targetattr="ipaProtectedOperation;read_keys") ... ... userattr=ipaAllowedToPerform;read_keys#USERDN) This ACI matches only if the service object hosts a new attribute named ipaAllowedToPerform that holds the DN of the user attempting the operation. Resolves: https://fedorahosted.org/freeipa/ticket/3859 Reviewed-By: Nathaniel McCallum <npmccallum@redhat.com>
Diffstat (limited to 'daemons')
-rw-r--r--daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c571
1 files changed, 571 insertions, 0 deletions
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
index 0b084a4d1..90a92f1ef 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
@@ -1268,6 +1268,571 @@ free_and_return:
return SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
}
+/* Format of getkeytab request
+ *
+ * KeytabGetRequest ::= CHOICE {
+ * newkeys [0] Newkeys,
+ * curkeys [1] CurrentKeys,
+ * reply [2] Reply
+ * }
+ *
+ * NewKeys ::= SEQUENCE {
+ * serviceIdentity [0] OCTET STRING,
+ * enctypes [1] SEQUENCE OF Int16
+ * password [2] OCTET STRING OPTIONAL,
+ * }
+ *
+ * CurrentKeys ::= SEQUENCE {
+ * serviceIdentity [0] OCTET STRING,
+ * }
+ */
+
+#define GK_REQUEST_NEWKEYS (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 0)
+#define GK_REQUEST_CURKEYS (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+#define GKREQ_SVCNAME_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+#define GKREQ_ENCTYPES_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+#define GKREQ_PASSWORD_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 2)
+
+static int decode_getkeytab_request(struct berval *extop, bool *wantold,
+ char **_svcname, char **_password,
+ krb5_key_salt_tuple **kenctypes,
+ int *num_kenctypes, char **_err_msg)
+{
+ int rc = LDAP_OPERATIONS_ERROR;
+ char *err_msg = NULL;
+ BerElement *ber = NULL;
+ ber_len_t tlen;
+ ber_tag_t rtag;
+ ber_tag_t ttag;
+ ber_tag_t ctag;
+ char *svcname = NULL;
+ char *password = NULL;
+ ber_int_t enctype;
+ krb5_key_salt_tuple *enctypes = NULL;
+ int num = 0;
+
+ ber = ber_init(extop);
+ if (ber == NULL) {
+ err_msg = "KeytabGet Request decode failed.\n";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ /* check this is a request */
+ rtag = ber_peek_tag(ber, &tlen);
+ if (rtag != GK_REQUEST_NEWKEYS && rtag != GK_REQUEST_CURKEYS) {
+ LOG_FATAL("ber_peek_tag failed, wrong request type\n");
+ err_msg = "Invalid payload.\n";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ /* ber parse code */
+ ttag = ber_scanf(ber, "{t[a]", &ctag, &svcname);
+ if (ttag == LBER_ERROR || ctag != GKREQ_SVCNAME_TAG) {
+ LOG_FATAL("ber_scanf failed to decode service name\n");
+ err_msg = "Invalid payload.\n";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ if (rtag == GK_REQUEST_CURKEYS) {
+ rc = LDAP_SUCCESS;
+ goto done;
+ }
+
+ ttag = ber_peek_tag(ber, &tlen);
+ if (ttag != GKREQ_ENCTYPES_TAG) {
+ LOG_FATAL("ber_peek_tag failed to find enctypes\n");
+ err_msg = "Invalid payload.\n";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ ttag = ber_peek_tag(ber, &tlen);
+ for (num = 0; ttag == LBER_INTEGER; num++) {
+ if ((num % 10) == 0) {
+ /* allocate space for at least 10 more enctypes */
+ enctypes = realloc(enctypes,
+ (num + 10) * sizeof(krb5_key_salt_tuple));
+ if (!enctypes) {
+ LOG_FATAL("allocation failed\n");
+ err_msg = "Internal error\n";
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+ }
+
+ ttag = ber_scanf(ber, "i", &enctype);
+ if (ttag == LBER_ERROR) {
+ LOG_FATAL("ber_scanf failed to decode enctype\n");
+ err_msg = "Invalid payload.\n";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ enctypes[num].ks_enctype = enctype;
+ enctypes[num].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
+ ttag = ber_peek_tag(ber, &tlen);
+ }
+
+ /* ttag peek done as last step of the previous for loop */
+ if (ttag == GKREQ_PASSWORD_TAG) {
+ /* optional password present */
+ ttag = ber_scanf(ber, "[a]", &password);
+ if (ttag == LBER_ERROR) {
+ LOG_FATAL("ber_scanf failed to decode password\n");
+ err_msg = "Invalid payload.\n";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ }
+
+ rc = LDAP_SUCCESS;
+
+done:
+ if (rc != LDAP_SUCCESS) {
+ free(password);
+ free(svcname);
+ *_err_msg = err_msg;
+ } else {
+ *_password = password;
+ *_svcname = svcname;
+ *wantold = (rtag == GK_REQUEST_CURKEYS);
+ *kenctypes = enctypes;
+ *num_kenctypes = num;
+ }
+ if (ber) ber_free(ber, 1);
+ return rc;
+}
+
+/* Format of getkeytab reply
+ *
+ * Reply ::= SEQUENCE {
+ * new_kvno Int32
+ * keys SEQUENCE OF KrbKey,
+ * }
+ *
+ * KrbKey ::= SEQUENCE {
+ * key [0] EncryptionKey,
+ * salt [1] KrbSalt OPTIONAL,
+ * s2kparams [2] OCTET STRING OPTIONAL,
+ * }
+ *
+ * EncryptionKey ::= SEQUENCE {
+ * keytype [0] Int32,
+ * keyvalue [1] OCTET STRING
+ * }
+ *
+ * KrbSalt ::= SEQUENCE {
+ * type [0] Int32,
+ * salt [1] OCTET STRING
+ * }
+ */
+
+#define GK_REPLY_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 2)
+#define GKREP_KEY_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 0)
+#define GKREP_SALT_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+#define GKREP_S2KPARAMS_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 2)
+#define GKREP_KEYTYPE_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 0)
+#define GKREP_KEYVALUE_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+#define GKREP_SALTTYPE_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 0)
+#define GKREP_SALTVALUE_TAG (LBER_CLASS_CONTEXT | LBER_CONSTRUCTED | 1)
+
+static int encode_getkeytab_reply(krb5_context krbctx,
+ krb5_keyblock *kmkey, int mkvno,
+ krb5_key_data *keys, int num_keys,
+ struct berval **_bvp)
+{
+ int rc = LDAP_OPERATIONS_ERROR;
+ struct berval *bvp = NULL;
+ BerElement *ber = NULL;
+ ber_int_t kvno;
+ krb5_data plain = { 0 };
+
+ ber = ber_alloc();
+ if (!ber) {
+ LOG_OOM();
+ goto done;
+ }
+
+ /* uses last key kvno */
+ kvno = keys[num_keys-1].key_data_kvno;
+
+ rc = ber_printf(ber, "t{i{", GK_REPLY_TAG, kvno);
+ if (rc == -1) {
+ rc = LDAP_OPERATIONS_ERROR;
+ LOG_FATAL("Failed to initiate key buffer\n");
+ goto done;
+ }
+
+ for (int i = 0; i < num_keys; i++) {
+ krb5_enc_data cipher = { 0 };
+ krb5_int16 plen;
+ void *p;
+
+ /* retrieve plain key */
+ memcpy(&plen, keys[i].key_data_contents[0], 2);
+ cipher.ciphertext.data = (char *)keys[i].key_data_contents[0] + 2;
+ cipher.ciphertext.length = keys[i].key_data_length[0] - 2;
+ cipher.enctype = kmkey->enctype;
+ cipher.kvno = mkvno;
+
+ plain.length = le16toh(plen);
+ p = realloc(plain.data, plain.length);
+ if (!p) {
+ LOG_FATAL("Failed to allocate plain buffer\n");
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+ plain.data = p;
+
+ rc = krb5_c_decrypt(krbctx, kmkey, 0, 0, &cipher, &plain);
+ if (rc) {
+ LOG_FATAL("Failed to decrypt keys\n");
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ rc = ber_printf(ber,
+ "{t[{t[i]t[o]}]",
+ GKREP_KEY_TAG,
+ GKREP_KEYTYPE_TAG,
+ (ber_int_t)keys[i].key_data_type[0],
+ GKREP_KEYVALUE_TAG,
+ plain.data, (ber_len_t)plain.length);
+ if (rc == -1) {
+ LOG_FATAL("Failed to encode key data\n");
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ /* if salt available, add it */
+ if (keys[i].key_data_length[1] != 0) {
+ rc = ber_printf(ber,
+ "t[{t[i]t[o]}]",
+ GKREP_SALT_TAG,
+ GKREP_SALTTYPE_TAG,
+ (ber_int_t)keys[i].key_data_type[1],
+ GKREP_SALTVALUE_TAG,
+ keys[i].key_data_contents[1],
+ (ber_len_t)keys[i].key_data_length[1]);
+ if (rc == -1) {
+ LOG_FATAL("Failed to encode salt data\n");
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+ }
+
+ rc = ber_printf(ber, "}");
+ if (rc == -1) {
+ LOG_FATAL("Failed to encode data\n");
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+ }
+
+ rc = ber_printf(ber, "}}");
+ if (rc == -1) {
+ LOG_FATAL("Failed to terminate key buffer\n");
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ rc = ber_flatten(ber, &bvp);
+ if (rc == -1) {
+ LOG_FATAL("Failed to encode key buffer\n");
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ rc = LDAP_SUCCESS;
+
+done:
+ if (rc != LDAP_SUCCESS) {
+ if (bvp) ber_bvfree(bvp);
+ } else {
+ *_bvp = bvp;
+ }
+ if (ber) ber_free(ber, 1);
+ free(plain.data);
+ return rc;
+}
+
+static int get_decoded_key_data(char *svcname,
+ krb5_key_data **_keys, int *_num_keys,
+ int *_mkvno, char **_err_msg)
+{
+ int rc = LDAP_OPERATIONS_ERROR;
+ char *err_msg = NULL;
+ krb5_key_data *keys = NULL;
+ int num_keys = 0;
+ int mkvno = 0;
+ Slapi_Entry *target = NULL;
+ Slapi_Attr *attr;
+ Slapi_Value *keys_value;
+ const struct berval *encoded_keys;
+
+ target = get_entry_by_principal(svcname);
+ if (!target) {
+ err_msg = "PrincipalName disappeared while processing.\n";
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ rc = slapi_entry_attr_find(target, "krbPrincipalKey", &attr);
+ if (rc) {
+ err_msg = "krbPrincipalKey not found\n";
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto done;
+ }
+ rc = slapi_attr_first_value(attr, &keys_value);
+ if (rc) {
+ err_msg = "Error retrieving krbPrincipalKey\n";
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+ encoded_keys = slapi_value_get_berval(keys_value);
+ if (!encoded_keys) {
+ err_msg = "Error retrieving encoded krbPrincipalKey\n";
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ rc = ber_decode_krb5_key_data(discard_const(encoded_keys),
+ &mkvno, &num_keys, &keys);
+ if (rc) {
+ err_msg = "Error retrieving decoded krbPrincipalKey\n";
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ if (num_keys <= 0) {
+ err_msg = "No krbPrincipalKeys available\n";
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ rc = LDAP_SUCCESS;
+
+done:
+ if (rc != LDAP_SUCCESS) {
+ if (keys) ipa_krb5_free_key_data(keys, num_keys);
+ *_err_msg = err_msg;
+ } else {
+ *_mkvno = mkvno;
+ *_keys = keys;
+ *_num_keys = num_keys;
+ }
+ if (target) slapi_entry_free(target);
+ return rc;
+}
+
+#define WRITEKEYS_OP_CHECK "ipaProtectedOperation;write_keys"
+#define READKEYS_OP_CHECK "ipaProtectedOperation;read_keys"
+
+/* Password Modify Extended operation plugin function */
+static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
+{
+ char *bind_dn = NULL;
+ char *err_msg = NULL;
+ int rc = 0;
+ krb5_context krbctx = NULL;
+ krb5_error_code krberr;
+ struct berval *extop_value = NULL;
+ BerElement *ber = NULL;
+ char *service_name = NULL;
+ char *svcname;
+ Slapi_Entry *target_entry = NULL;
+ bool acl_ok = false;
+ char *password = NULL;
+ int num_kenctypes = 0;
+ krb5_key_salt_tuple *kenctypes = NULL;
+ int mkvno = 0;
+ int num_keys = 0;
+ krb5_key_data *keys = NULL;
+ struct ipapwd_data data = { 0 };
+ Slapi_Value **svals = NULL;
+ struct berval *bvp = NULL;
+ LDAPControl new_ctrl;
+ bool wantold = false;
+
+ /* Get Bind DN */
+ slapi_pblock_get(pb, SLAPI_CONN_DN, &bind_dn);
+
+ /* If the connection is bound anonymously, we must refuse to process
+ * this operation. */
+ if (bind_dn == NULL || *bind_dn == '\0') {
+ /* Refuse the operation because they're bound anonymously */
+ err_msg = "Anonymous Binds are not allowed.\n";
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ goto free_and_return;
+ }
+
+ krberr = krb5_init_context(&krbctx);
+ if (krberr) {
+ LOG_FATAL("krb5_init_context failed\n");
+ rc = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
+ }
+
+ /* Get the ber value of the extended operation */
+ slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
+ if (!extop_value) {
+ LOG_FATAL("Failed to retrieve extended op value from pblock\n");
+ err_msg = "Failed to retrieve extended operation value\n";
+ rc = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
+ }
+
+ rc = decode_getkeytab_request(extop_value, &wantold, &service_name,
+ &password, &kenctypes, &num_kenctypes,
+ &err_msg);
+ if (rc != LDAP_SUCCESS) {
+ goto free_and_return;
+ }
+
+ /* make sure it is a valid name */
+ svcname = check_service_name(krbctx, service_name);
+ if (!svcname) {
+ rc = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
+ }
+ slapi_ch_free_string(&service_name);
+ service_name = svcname;
+
+ /* check entry */
+
+ /* get Entry by krbPrincipalName */
+ target_entry = get_entry_by_principal(service_name);
+ if (!target_entry) {
+ err_msg = "PrincipalName not found.\n";
+ rc = LDAP_NO_SUCH_OBJECT;
+ goto free_and_return;
+ }
+
+ /* ok access allowed */
+ /* do we need to create new keys ? */
+ if (wantold) { /* requesting to retrieve existing ones */
+
+ /* check if we are allowed to *read* keys */
+ acl_ok = is_allowed_to_access_attr(pb, bind_dn, target_entry,
+ READKEYS_OP_CHECK, NULL,
+ SLAPI_ACL_READ);
+ if (!acl_ok) {
+ LOG_FATAL("Not allowed to retrieve keytab on [%s]!\n",
+ service_name);
+ err_msg = "Insufficient access rights\n";
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ goto free_and_return;
+ }
+
+ } else {
+
+ /* check if we are allowed to *write* keys */
+ acl_ok = is_allowed_to_access_attr(pb, bind_dn, target_entry,
+ WRITEKEYS_OP_CHECK, NULL,
+ SLAPI_ACL_WRITE);
+ if (!acl_ok) {
+ LOG_FATAL("Not allowed to set keytab on [%s]!\n",
+ service_name);
+ err_msg = "Insufficient access rights\n";
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ goto free_and_return;
+ }
+
+ for (int i = 0; i < num_kenctypes; i++) {
+
+ /* Check if supported */
+ for (int j = 0; j < krbcfg->num_supp_encsalts; j++) {
+ if (kenctypes[i].ks_enctype ==
+ krbcfg->supp_encsalts[j].ks_enctype) {
+ continue;
+ }
+ }
+ /* Unsupported, filter out */
+ for (int j = i; j + 1 < num_kenctypes; j++) {
+ kenctypes[j].ks_enctype = kenctypes[j + 1].ks_enctype;
+ kenctypes[j].ks_salttype = kenctypes[j + 1].ks_salttype;
+ }
+ num_kenctypes--;
+ i--;
+ }
+
+ /* check if we have any left */
+ if (num_kenctypes == 0 && kenctypes != NULL) {
+ LOG_FATAL("keyset filtering rejected all proposed keys\n");
+ err_msg = "All enctypes provided are unsupported";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto free_and_return;
+ }
+
+ /* only target is used, leave everything else NULL,
+ * if password is not provided we want to generate a random key */
+ data.target = target_entry;
+ data.password = password;
+
+ svals = ipapwd_encrypt_encode_key(krbcfg, &data,
+ kenctypes ? num_kenctypes :
+ krbcfg->num_pref_encsalts,
+ kenctypes ? kenctypes :
+ krbcfg->pref_encsalts,
+ &err_msg);
+ if (!svals) {
+ rc = LDAP_OPERATIONS_ERROR;
+ LOG_FATAL("encrypt_encode_keys failed!\n");
+ err_msg = "Internal error while encrypting keys\n";
+ goto free_and_return;
+ }
+
+ rc = store_new_keys(target_entry, service_name, bind_dn, svals,
+ &err_msg);
+ if (rc != LDAP_SUCCESS) {
+ goto free_and_return;
+ }
+ }
+
+ rc = get_decoded_key_data(service_name,
+ &keys, &num_keys, &mkvno, &err_msg);
+ if (rc != LDAP_SUCCESS) {
+ goto free_and_return;
+ }
+
+ rc = encode_getkeytab_reply(krbctx, krbcfg->kmkey, mkvno,
+ keys, num_keys, &bvp);
+ if (rc != LDAP_SUCCESS) {
+ err_msg = "Internal Error.\n";
+ goto free_and_return;
+ }
+
+ new_ctrl.ldctl_oid = KEYTAB_GET_OID;
+ new_ctrl.ldctl_value = *bvp;
+ new_ctrl.ldctl_iscritical = 0;
+ rc = slapi_pblock_set(pb, SLAPI_ADD_RESCONTROL, &new_ctrl);
+
+free_and_return:
+ if (rc == LDAP_SUCCESS) err_msg = NULL;
+ LOG("%s", err_msg ? err_msg : "success");
+ slapi_send_ldap_result(pb, rc, NULL, err_msg, 0, NULL);
+
+ /* Free anything that we allocated above */
+ if (krbctx) krb5_free_context(krbctx);
+ free(kenctypes);
+ free(service_name);
+ free(password);
+ if (target_entry) slapi_entry_free(target_entry);
+ if (keys) ipa_krb5_free_key_data(keys, num_keys);
+ if (svals) {
+ for (int i = 0; svals[i]; i++) {
+ slapi_value_free(&svals[i]);
+ }
+ free(svals);
+ }
+ if (ber) ber_free(ber, 1);
+ if (bvp) ber_bvfree(bvp);
+
+ return SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
+}
+
static int ipapwd_extop(Slapi_PBlock *pb)
{
struct ipapwd_krbcfg *krbcfg = NULL;
@@ -1305,6 +1870,11 @@ static int ipapwd_extop(Slapi_PBlock *pb)
free_ipapwd_krbcfg(&krbcfg);
return ret;
}
+ if (strcasecmp(oid, KEYTAB_GET_OID) == 0) {
+ ret = ipapwd_getkeytab(pb, krbcfg);
+ free_ipapwd_krbcfg(&krbcfg);
+ return ret;
+ }
errMesg = "Request OID does not match supported OIDs.\n";
rc = LDAP_OPERATIONS_ERROR;
@@ -1438,6 +2008,7 @@ done:
static char *ipapwd_oid_list[] = {
EXOP_PASSWD_OID,
KEYTAB_SET_OID,
+ KEYTAB_GET_OID,
NULL
};