diff options
Diffstat (limited to 'src/lib/kadm5')
-rw-r--r-- | src/lib/kadm5/Makefile.in | 4 | ||||
-rw-r--r-- | src/lib/kadm5/admin.h | 61 | ||||
-rw-r--r-- | src/lib/kadm5/alt_prof.c | 63 | ||||
-rw-r--r-- | src/lib/kadm5/clnt/Makefile.in | 7 | ||||
-rw-r--r-- | src/lib/kadm5/clnt/client_init.c | 52 | ||||
-rw-r--r-- | src/lib/kadm5/clnt/libkadm5clnt.exports | 1 | ||||
-rw-r--r-- | src/lib/kadm5/srv/Makefile.in | 8 | ||||
-rw-r--r-- | src/lib/kadm5/srv/libkadm5srv.exports | 1 | ||||
-rw-r--r-- | src/lib/kadm5/srv/server_acl.c | 3 | ||||
-rw-r--r-- | src/lib/kadm5/srv/server_acl.h | 4 | ||||
-rw-r--r-- | src/lib/kadm5/srv/server_init.c | 38 |
11 files changed, 204 insertions, 38 deletions
diff --git a/src/lib/kadm5/Makefile.in b/src/lib/kadm5/Makefile.in index bcb46cede3..8cc931acac 100644 --- a/src/lib/kadm5/Makefile.in +++ b/src/lib/kadm5/Makefile.in @@ -171,10 +171,12 @@ alt_prof.so alt_prof.po $(OUTPRE)alt_prof.$(OBJEXT): \ $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \ $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \ $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \ + $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \ $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \ $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \ $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \ - $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \ + $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_log.h \ + $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \ $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \ $(SRCTOP)/include/socket-utils.h alt_prof.c str_conv.so str_conv.po $(OUTPRE)str_conv.$(OBJEXT): \ diff --git a/src/lib/kadm5/admin.h b/src/lib/kadm5/admin.h index 06a77dce64..9013305b69 100644 --- a/src/lib/kadm5/admin.h +++ b/src/lib/kadm5/admin.h @@ -48,6 +48,7 @@ #define KADM5_ADMIN_SERVICE "kadmin/admin" #define KADM5_CHANGEPW_SERVICE "kadmin/changepw" #define KADM5_HIST_PRINCIPAL "kadmin/history" +#define KADM5_KIPROP_HOST_SERVICE "kiprop" typedef krb5_principal kadm5_princ_t; typedef char *kadm5_policy_t; @@ -107,32 +108,37 @@ typedef long kadm5_ret_t; #define KADM5_REF_COUNT 0x080000 /* kadm5_config_params */ -#define KADM5_CONFIG_REALM 0x000001 -#define KADM5_CONFIG_DBNAME 0x000002 -#define KADM5_CONFIG_MKEY_NAME 0x000004 -#define KADM5_CONFIG_MAX_LIFE 0x000008 -#define KADM5_CONFIG_MAX_RLIFE 0x000010 -#define KADM5_CONFIG_EXPIRATION 0x000020 -#define KADM5_CONFIG_FLAGS 0x000040 -#define KADM5_CONFIG_ADMIN_KEYTAB 0x000080 -#define KADM5_CONFIG_STASH_FILE 0x000100 -#define KADM5_CONFIG_ENCTYPE 0x000200 -#define KADM5_CONFIG_ADBNAME 0x000400 -#define KADM5_CONFIG_ADB_LOCKFILE 0x000800 -/*#define KADM5_CONFIG_PROFILE 0x001000*/ -#define KADM5_CONFIG_ACL_FILE 0x002000 -#define KADM5_CONFIG_KADMIND_PORT 0x004000 -#define KADM5_CONFIG_ENCTYPES 0x008000 -#define KADM5_CONFIG_ADMIN_SERVER 0x010000 -#define KADM5_CONFIG_DICT_FILE 0x020000 -#define KADM5_CONFIG_MKEY_FROM_KBD 0x040000 -#define KADM5_CONFIG_KPASSWD_PORT 0x080000 -#define KADM5_CONFIG_OLD_AUTH_GSSAPI 0x100000 -#define KADM5_CONFIG_NO_AUTH 0x200000 -#define KADM5_CONFIG_AUTH_NOFALLBACK 0x400000 +#define KADM5_CONFIG_REALM 0x00000001 +#define KADM5_CONFIG_DBNAME 0x00000002 +#define KADM5_CONFIG_MKEY_NAME 0x00000004 +#define KADM5_CONFIG_MAX_LIFE 0x00000008 +#define KADM5_CONFIG_MAX_RLIFE 0x00000010 +#define KADM5_CONFIG_EXPIRATION 0x00000020 +#define KADM5_CONFIG_FLAGS 0x00000040 +#define KADM5_CONFIG_ADMIN_KEYTAB 0x00000080 +#define KADM5_CONFIG_STASH_FILE 0x00000100 +#define KADM5_CONFIG_ENCTYPE 0x00000200 +#define KADM5_CONFIG_ADBNAME 0x00000400 +#define KADM5_CONFIG_ADB_LOCKFILE 0x00000800 +/*#define KADM5_CONFIG_PROFILE 0x00001000*/ +#define KADM5_CONFIG_ACL_FILE 0x00002000 +#define KADM5_CONFIG_KADMIND_PORT 0x00004000 +#define KADM5_CONFIG_ENCTYPES 0x00008000 +#define KADM5_CONFIG_ADMIN_SERVER 0x00010000 +#define KADM5_CONFIG_DICT_FILE 0x00020000 +#define KADM5_CONFIG_MKEY_FROM_KBD 0x00040000 +#define KADM5_CONFIG_KPASSWD_PORT 0x00080000 +#define KADM5_CONFIG_OLD_AUTH_GSSAPI 0x00100000 +#define KADM5_CONFIG_NO_AUTH 0x00200000 +#define KADM5_CONFIG_AUTH_NOFALLBACK 0x00400000 #ifdef notyet /* Novell */ -#define KADM5_CONFIG_KPASSWD_SERVER 0x800000 +#define KADM5_CONFIG_KPASSWD_SERVER 0x00800000 #endif +#define KADM5_CONFIG_IPROP_ENABLED 0x01000000 +#define KADM5_CONFIG_ULOG_SIZE 0x02000000 +#define KADM5_CONFIG_POLL_TIME 0x04000000 +#define KADM5_CONFIG_IPROP_LOGFILE 0x08000000 +#define KADM5_CONFIG_IPROP_PORT 0x10000000 /* * permission bits */ @@ -250,6 +256,13 @@ typedef struct _kadm5_config_params { krb5_flags flags; krb5_key_salt_tuple *keysalts; krb5_int32 num_keysalts; + + bool_t iprop_enabled; + uint32_t iprop_ulogsize; + krb5_deltat iprop_poll_time; + char * iprop_logfile; +/* char * iprop_server;*/ + int iprop_port; } kadm5_config_params; /*********************************************************************** diff --git a/src/lib/kadm5/alt_prof.c b/src/lib/kadm5/alt_prof.c index c56ceac2ff..c8bc6b052d 100644 --- a/src/lib/kadm5/alt_prof.c +++ b/src/lib/kadm5/alt_prof.c @@ -24,6 +24,10 @@ * or implied warranty. * */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ /* * alt_prof.c - Implement alternate profile file handling. @@ -33,6 +37,7 @@ #include "adm_proto.h" #include <stdio.h> #include <ctype.h> +#include <kdb_log.h> static krb5_key_salt_tuple *copy_key_salt_tuple(ksalt, len) krb5_key_salt_tuple *ksalt; @@ -381,7 +386,7 @@ get_string_param(char **param_out, char *param_in, } /* * Similar, for (host-order) port number, if not already set in the - * output field; default is required. + * output field; default_value==0 means no default. */ static void get_port_param(int *param_out, int param_in, @@ -402,7 +407,7 @@ get_port_param(int *param_out, int param_in, !krb5_aprof_get_int32(aprofile, hierarchy, TRUE, &ivalue)) { *param_out = ivalue; *mask_out |= mask_bit; - } else { + } else if (default_value) { *param_out = default_value; *mask_out |= mask_bit; } @@ -475,6 +480,7 @@ krb5_error_code kadm5_get_config_params(context, use_kdc_config, krb5_pointer aprofile = 0; const char *hierarchy[4]; char *svalue; + krb5_int32 ivalue; kadm5_config_params params, empty_params; krb5_error_code kret = 0; @@ -714,6 +720,59 @@ krb5_error_code kadm5_get_config_params(context, use_kdc_config, krb5_xfree(svalue); } + hierarchy[2] = "iprop_enable"; + + params.iprop_enabled = FALSE; + params.mask |= KADM5_CONFIG_IPROP_ENABLED; + + if (params_in->mask & KADM5_CONFIG_IPROP_ENABLED) { + params.mask |= KADM5_CONFIG_IPROP_ENABLED; + params.iprop_enabled = params_in->iprop_enabled; + } else { + krb5_boolean bvalue; + if (aprofile && + !krb5_aprof_get_boolean(aprofile, hierarchy, TRUE, &bvalue)) { + params.iprop_enabled = bvalue; + params.mask |= KADM5_CONFIG_IPROP_ENABLED; + } + } + + if (!GET_STRING_PARAM(iprop_logfile, KADM5_CONFIG_IPROP_LOGFILE, + "iprop_logfile", NULL)) { + if (params.mask & KADM5_CONFIG_DBNAME) { + if (asprintf(¶ms.iprop_logfile, "%s.ulog", params.dbname) >= 0) { + params.mask |= KADM5_CONFIG_IPROP_LOGFILE; + } + } + } + + GET_PORT_PARAM(iprop_port, KADM5_CONFIG_IPROP_PORT, + "iprop_port", 0); + + hierarchy[2] = "iprop_master_ulogsize"; + + params.iprop_ulogsize = DEF_ULOGENTRIES; + params.mask |= KADM5_CONFIG_ULOG_SIZE; + + if (params_in->mask & KADM5_CONFIG_ULOG_SIZE) { + params.mask |= KADM5_CONFIG_ULOG_SIZE; + params.iprop_ulogsize = params_in->iprop_ulogsize; + } else { + if (aprofile && !krb5_aprof_get_int32(aprofile, hierarchy, + TRUE, &ivalue)) { + if (ivalue > MAX_ULOGENTRIES) + params.iprop_ulogsize = MAX_ULOGENTRIES; + else if (ivalue <= 0) + params.iprop_ulogsize = DEF_ULOGENTRIES; + else + params.iprop_ulogsize = ivalue; + params.mask |= KADM5_CONFIG_ULOG_SIZE; + } + } + + GET_DELTAT_PARAM(iprop_poll_time, KADM5_CONFIG_POLL_TIME, + "iprop_slave_poll", 2 * 60); /* 2m */ + *params_out = params; cleanup: diff --git a/src/lib/kadm5/clnt/Makefile.in b/src/lib/kadm5/clnt/Makefile.in index 8c0bce1056..69679a9c89 100644 --- a/src/lib/kadm5/clnt/Makefile.in +++ b/src/lib/kadm5/clnt/Makefile.in @@ -6,8 +6,8 @@ LOCALINCLUDES = -I$(BUILDTOP)/include/kadm5 DEFS= LIBBASE=kadm5clnt -LIBMAJOR=5 -LIBMINOR=1 +LIBMAJOR=6 +LIBMINOR=0 STOBJLISTS=../OBJS.ST OBJS.ST SHLIB_EXPDEPS=\ $(TOPLIBD)/libgssrpc$(SHLIBEXT) \ @@ -130,7 +130,8 @@ client_init.so client_init.po $(OUTPRE)client_init.$(OBJEXT): \ $(SRCTOP)/include/gssrpc/clnt.h $(SRCTOP)/include/gssrpc/rename.h \ $(SRCTOP)/include/gssrpc/rpc.h $(SRCTOP)/include/gssrpc/rpc_msg.h \ $(SRCTOP)/include/gssrpc/svc.h $(SRCTOP)/include/gssrpc/svc_auth.h \ - $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/k5-err.h \ + $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/iprop.h \ + $(SRCTOP)/include/iprop_hdr.h $(SRCTOP)/include/k5-err.h \ $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \ $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \ $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \ diff --git a/src/lib/kadm5/clnt/client_init.c b/src/lib/kadm5/clnt/client_init.c index 8fc3dc1e28..d5131cfdad 100644 --- a/src/lib/kadm5/clnt/client_init.c +++ b/src/lib/kadm5/clnt/client_init.c @@ -48,6 +48,8 @@ #include <kadm5/admin.h> #include <kadm5/kadm_rpc.h> #include "client_internal.h" +#include <iprop_hdr.h> +#include "iprop.h" #include <gssrpc/rpc.h> #include <gssapi/gssapi.h> @@ -165,6 +167,8 @@ static kadm5_ret_t _kadm5_init_any(char *client_name, struct hostent *hp; int fd; + char *iprop_svc; + int iprop_enable = 0; char full_svcname[BUFSIZ]; char *realm; @@ -296,15 +300,37 @@ static kadm5_ret_t _kadm5_init_any(char *client_name, goto cleanup; } + /* + * If the service_name and client_name are iprop-centric, + * we need to clnttcp_create to the appropriate RPC prog. + */ + iprop_svc = strdup(KIPROP_SVC_NAME); + if (iprop_svc == NULL) + return ENOMEM; + + if (service_name != NULL && + (strstr(service_name, iprop_svc) != NULL) && + (strstr(client_name, iprop_svc) != NULL)) + iprop_enable = 1; + else + iprop_enable = 0; + memset(&addr, 0, sizeof(addr)); addr.sin_family = hp->h_addrtype; (void) memcpy((char *) &addr.sin_addr, (char *) hp->h_addr, sizeof(addr.sin_addr)); - addr.sin_port = htons((u_short) handle->params.kadmind_port); + if (iprop_enable) + addr.sin_port = htons((u_short) handle->params.iprop_port); + else + addr.sin_port = htons((u_short) handle->params.kadmind_port); fd = RPC_ANYSOCK; - - handle->clnt = clnttcp_create(&addr, KADM, KADMVERS, &fd, 0, 0); + + if (iprop_enable) { + handle->clnt = clnttcp_create(&addr, KRB5_IPROP_PROG, KRB5_IPROP_VERS, + &fd, 0, 0); + } else + handle->clnt = clnttcp_create(&addr, KADM, KADMVERS, &fd, 0, 0); if (handle->clnt == NULL) { code = KADM5_RPC_ERROR; #ifdef DEBUG @@ -326,6 +352,16 @@ static kadm5_ret_t _kadm5_init_any(char *client_name, if (code) goto error; + /* + * Bypass the remainder of the code and return straightaway + * if the gss service requested is kiprop + */ + if (iprop_enable == 1) { + code = 0; + *server_handle = (void *) handle; + goto cleanup; + } + r = init_2(&handle->api_version, handle->clnt); if (r == NULL) { code = KADM5_RPC_ERROR; @@ -794,3 +830,13 @@ krb5_error_code kadm5_init_krb5_context (krb5_context *ctx) { return krb5_init_context(ctx); } + +/* + * Stub function for kadmin. It was created to eliminate the dependency on + * libkdb's ulog functions. The srv equivalent makes the actual calls. + */ +krb5_error_code +kadm5_init_iprop(void *handle) +{ + return (0); +} diff --git a/src/lib/kadm5/clnt/libkadm5clnt.exports b/src/lib/kadm5/clnt/libkadm5clnt.exports index f7f873e292..7f11f320ac 100644 --- a/src/lib/kadm5/clnt/libkadm5clnt.exports +++ b/src/lib/kadm5/clnt/libkadm5clnt.exports @@ -129,3 +129,4 @@ xdr_setkey3_arg xdr_setkey_arg xdr_setv4key_arg xdr_ui_4 +kadm5_init_iprop diff --git a/src/lib/kadm5/srv/Makefile.in b/src/lib/kadm5/srv/Makefile.in index 21628bd7e5..9889c1e222 100644 --- a/src/lib/kadm5/srv/Makefile.in +++ b/src/lib/kadm5/srv/Makefile.in @@ -12,8 +12,8 @@ DEFS= ##DOSLIBNAME = libkadm5srv.lib LIBBASE=kadm5srv -LIBMAJOR=5 -LIBMINOR=1 +LIBMAJOR=6 +LIBMINOR=0 STOBJLISTS=../OBJS.ST OBJS.ST SHLIB_EXPDEPS=\ @@ -191,10 +191,12 @@ server_init.so server_init.po $(OUTPRE)server_init.$(OBJEXT): \ $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \ $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \ $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \ + $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \ $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \ $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \ $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \ - $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \ + $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_log.h \ + $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \ $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \ $(SRCTOP)/include/socket-utils.h $(srcdir)/../../gssapi/generic/gssapiP_generic.h \ $(srcdir)/../../gssapi/generic/gssapi_generic.h $(srcdir)/../../gssapi/krb5/gssapiP_krb5.h \ diff --git a/src/lib/kadm5/srv/libkadm5srv.exports b/src/lib/kadm5/srv/libkadm5srv.exports index 23a4ee1e56..a4d2156f77 100644 --- a/src/lib/kadm5/srv/libkadm5srv.exports +++ b/src/lib/kadm5/srv/libkadm5srv.exports @@ -161,3 +161,4 @@ xdr_setkey3_arg xdr_setkey_arg xdr_setv4key_arg xdr_ui_4 +kadm5_init_iprop diff --git a/src/lib/kadm5/srv/server_acl.c b/src/lib/kadm5/srv/server_acl.c index bcfe35f848..6cc9492c37 100644 --- a/src/lib/kadm5/srv/server_acl.c +++ b/src/lib/kadm5/srv/server_acl.c @@ -1,7 +1,7 @@ /* * lib/kadm5/srv/server_acl.c * - * Copyright 1995-2004, 2007 by the Massachusetts Institute of Technology. + * Copyright 1995-2004, 2007, 2008 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may @@ -66,6 +66,7 @@ static const aop_t acl_op_table[] = { { 'c', ACL_CHANGEPW }, { 'i', ACL_INQUIRE }, { 'l', ACL_LIST }, + { 'p', ACL_IPROP }, { 's', ACL_SETKEY }, { 'x', ACL_ALL_MASK }, { '*', ACL_ALL_MASK }, diff --git a/src/lib/kadm5/srv/server_acl.h b/src/lib/kadm5/srv/server_acl.h index 5024730cf6..b0ed0bf3dd 100644 --- a/src/lib/kadm5/srv/server_acl.h +++ b/src/lib/kadm5/srv/server_acl.h @@ -1,7 +1,7 @@ /* * lib/kadm5/srv/server_acl.h * - * Copyright 1995-2004, 2007 by the Massachusetts Institute of Technology. + * Copyright 1995-2004, 2007, 2008 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may @@ -58,6 +58,7 @@ /* #define ACL_EXTRACT 64 */ #define ACL_LIST 128 #define ACL_SETKEY 256 +#define ACL_IPROP 512 #define ACL_RENAME (ACL_ADD+ACL_DELETE) #define ACL_ALL_MASK (ACL_ADD | \ @@ -66,6 +67,7 @@ ACL_CHANGEPW | \ ACL_INQUIRE | \ ACL_LIST | \ + ACL_IPROP | \ ACL_SETKEY) typedef struct _restriction { diff --git a/src/lib/kadm5/srv/server_init.c b/src/lib/kadm5/srv/server_init.c index dbb7ff78e6..febf79bdf8 100644 --- a/src/lib/kadm5/srv/server_init.c +++ b/src/lib/kadm5/srv/server_init.c @@ -4,6 +4,10 @@ * $Id$ * $Source$ */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; @@ -16,8 +20,10 @@ static char *rcsid = "$Header$"; #include "k5-int.h" /* needed for gssapiP_krb5.h */ #include <kadm5/admin.h> #include <krb5.h> +#include <kdb_log.h> #include "server_internal.h" #include "osconf.h" +#include "iprop_hdr.h" /* * Function check_handle @@ -238,12 +244,26 @@ kadm5_ret_t kadm5_init(char *client_name, char *pass, KADM5_CONFIG_MAX_LIFE | KADM5_CONFIG_MAX_RLIFE | \ KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_ENCTYPES) +#define IPROP_REQUIRED_PARAMS \ + (KADM5_CONFIG_IPROP_ENABLED | \ + KADM5_CONFIG_IPROP_LOGFILE | \ + KADM5_CONFIG_IPROP_PORT) + if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { krb5_free_context(handle->context); free_db_args(handle); free(handle); return KADM5_MISSING_CONF_PARAMS; } + if ((handle->params.mask & KADM5_CONFIG_IPROP_ENABLED) == KADM5_CONFIG_IPROP_ENABLED + && handle->params.iprop_enabled) { + if ((handle->params.mask & IPROP_REQUIRED_PARAMS) != IPROP_REQUIRED_PARAMS) { + krb5_free_context(handle->context); + free_db_args(handle); + free(handle); + return KADM5_MISSING_CONF_PARAMS; + } + } ret = krb5_set_default_realm(handle->context, handle->params.realm); if (ret) { @@ -430,3 +450,21 @@ krb5_error_code kadm5_init_krb5_context (krb5_context *ctx) } return krb5int_init_context_kdc(ctx); } + +krb5_error_code +kadm5_init_iprop(void *handle, char **db_args) +{ + kadm5_server_handle_t iprop_h; + krb5_error_code retval; + + iprop_h = handle; + if (iprop_h->params.iprop_enabled) { + ulog_set_role(iprop_h->context, IPROP_MASTER); + if ((retval = ulog_map(iprop_h->context, + iprop_h->params.iprop_logfile, + iprop_h->params.iprop_ulogsize, + FKCOMMAND, db_args)) != 0) + return (retval); + } + return (0); +} |