bug-inetutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bug-inetutils] Telnet(d) support for new Kerberos 5 implementation


From: Simon Josefsson
Subject: [bug-inetutils] Telnet(d) support for new Kerberos 5 implementation
Date: Mon, 23 Jun 2003 17:05:50 +0200
User-agent: Gnus/5.1003 (Gnus v5.10.3) Emacs/21.3.50 (gnu/linux)

This patch makes Inetutils support Shishi for Kerberos 5 in telnet(d).
Shishi is GPL, and doesn't link with GPL-incompatible libraries like
some krb5 implementation does.

Note that the patch in <address@hidden> is
needed, to protect against DNS manipulation attacks for krb5 in
telnet(d).  (This affects MIT and Heimdal too.)

This was written some time ago, and I didn't test it on many platforms
now, but I was able to use the client to log on against
Inetutils/Heimdal/MIT telnetd.

Thanks.

Index: README
===================================================================
RCS file: /cvsroot/inetutils/inetutils/README,v
retrieving revision 1.10
diff -u -p -r1.10 README
--- README      25 Dec 2001 18:47:44 -0000      1.10
+++ README      23 Jun 2003 14:49:46 -0000
@@ -47,6 +47,12 @@ not want to install these files.
 install setuid root to work correctly they use priviledge ports
 for communication. 
 
+3) If both Kerberos 5 (MIT and Heimdal) and Shishi is specified, the
+applications that have been ported to use Shishi will use Shishi only,
+and the applications that have not been ported to use Shishi will use
+MIT or Heimdal Kerberos.  If you want MIT or Heimdal instead of
+Shishi, don't specify --with-shishi.
+
 Some known deficiencies:
  o Many programs do not support long options, such as --version or --help.
  o The authentication and encryption options have not been tested.
Index: configure.ac
===================================================================
RCS file: /cvsroot/inetutils/inetutils/configure.ac,v
retrieving revision 1.18
diff -u -p -r1.18 configure.ac
--- configure.ac        24 May 2003 23:36:45 -0000      1.18
+++ configure.ac        23 Jun 2003 14:49:47 -0000
@@ -1,6 +1,6 @@
 # Configuration for inetutils
 #
-# Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2002 Free Software 
Foundation, Inc.
+# Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003 Free Software 
Foundation, Inc.
 #
 # Written by Miles Bader <address@hidden>
 #
@@ -63,6 +63,9 @@ AC_ARG_WITH(krb4, [  --with-krb4[[=PATH]
 AC_ARG_WITH(krb5, [  --with-krb5[[=PATH]]      Compile with Kerberos V],
             [KERBEROS_VERSION=5
              KERBEROS_DIR=$withval])
+AC_ARG_WITH(shishi, AC_HELP_STRING([--with-shishi[[=PATH]]],
+                                  [Compile with Shishi (Kerberos 5)]),
+            [SHISHI_DIR=$withval])
 AC_ARG_WITH(wrap, [  --with-wrap    add tcp wrapper support])
 AC_ARG_WITH(pam,  [  --with-pam    add support for PAM])
 
@@ -242,6 +245,7 @@ if test "$enable_encryption" = yes -o "$
   AH_TEMPLATE(KERBEROS, [Define to one if you have Kerberos])
   AH_TEMPLATE(KRB4, [Define to one if you have Kerberos IV])
   AH_TEMPLATE(KRB5, [Define to one if you have Kerberos V])
+  AH_TEMPLATE(SHISHI, [Define to 1 if you have Shishi])
   AH_TEMPLATE(DES_ENCRYPTION, [FIXME])
   
   if test "$KERBEROS_VERSION" = 4; then
@@ -275,6 +279,14 @@ if test "$enable_encryption" = yes -o "$
          ;;
       esac
     fi
+  fi
+  if test "$with_shishi" = "yes"; then
+    if test x$SHISHI_DIR != x; then
+      LIBAUTH="$LIBAUTH -L$SHISHI_DIR/lib"
+      INCAUTH="$INCAUTH -I$SHISHI_DIR/include "
+    fi
+    LIBAUTH="$LIBAUTH -lshishi"
+    AC_DEFINE(SHISHI)
   fi
 fi
 AC_SUBST(LIBAUTH)
Index: libtelnet/Makefile.am
===================================================================
RCS file: /cvsroot/inetutils/inetutils/libtelnet/Makefile.am,v
retrieving revision 1.5
diff -u -p -r1.5 Makefile.am
--- libtelnet/Makefile.am       8 Apr 2002 14:02:39 -0000       1.5
+++ libtelnet/Makefile.am       23 Jun 2003 14:49:47 -0000
@@ -4,6 +4,6 @@ INCLUDES = -I$(top_builddir)/include @IN
 
 noinst_LIBRARIES = libtelnet.a
 
-libtelnet_a_SOURCES = auth.c enc_des.c encrypt.c forward.c genget.c getent.c 
kerberos.c kerberos5.c misc.c read_passwd.c
+libtelnet_a_SOURCES = auth.c enc_des.c encrypt.c forward.c genget.c getent.c 
kerberos.c kerberos5.c misc.c read_passwd.c shishi.c
 
 noinst_HEADERS = auth-proto.h auth.h enc-proto.h encrypt.h key-proto.h 
misc-proto.h misc.h
Index: libtelnet/auth-proto.h
===================================================================
RCS file: /cvsroot/inetutils/inetutils/libtelnet/auth-proto.h,v
retrieving revision 1.3
diff -u -p -r1.3 auth-proto.h
--- libtelnet/auth-proto.h      6 Dec 2002 16:09:06 -0000       1.3
+++ libtelnet/auth-proto.h      23 Jun 2003 14:49:47 -0000
@@ -89,4 +89,14 @@ void kerberos5_reply P((TN_Authenticator
 int kerberos5_status P((TN_Authenticator *, char *, int));
 void kerberos5_printsub P((unsigned char *, int, unsigned char *, int));
 #endif
+
+#ifdef SHISHI
+int krb5shishi_init P((TN_Authenticator *, int));
+int krb5shishi_send P((TN_Authenticator *));
+void krb5shishi_is P((TN_Authenticator *, unsigned char *, int));
+void krb5shishi_reply P((TN_Authenticator *, unsigned char *, int));
+int krb5shishi_status P((TN_Authenticator *, char *, int));
+void krb5shishi_printsub P((unsigned char *, int, unsigned char *, int));
+void krb5shishi_cleanup P((TN_Authenticator *));
+#endif
 #endif
Index: libtelnet/auth.c
===================================================================
RCS file: /cvsroot/inetutils/inetutils/libtelnet/auth.c,v
retrieving revision 1.6
diff -u -p -r1.6 auth.c
--- libtelnet/auth.c    6 Dec 2002 16:10:07 -0000       1.6
+++ libtelnet/auth.c    23 Jun 2003 14:49:47 -0000
@@ -126,6 +126,24 @@ TN_Authenticator authenticators[] = {
                                spx_status,
                                spx_printsub },
 #endif
+#ifdef SHISHI
+       { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
+         krb5shishi_init,
+         krb5shishi_send,
+         krb5shishi_is,
+         krb5shishi_reply,
+         krb5shishi_status,
+         krb5shishi_printsub,
+         krb5shishi_cleanup },
+       { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
+         krb5shishi_init,
+         krb5shishi_send,
+         krb5shishi_is,
+         krb5shishi_reply,
+         krb5shishi_status,
+         krb5shishi_printsub,
+         krb5shishi_cleanup },
+#endif
 #ifdef KRB5
 # ifdef        ENCRYPTION
        { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
@@ -610,6 +628,8 @@ auth_finished(ap, result)
        TN_Authenticator *ap;
        int result;
 {
+       if (ap && ap->cleanup)
+               (*ap->cleanup) (ap);
        if (!(authenticated = ap))
                authenticated = &NoAuth;
        validuser = result;
Index: libtelnet/auth.h
===================================================================
RCS file: /cvsroot/inetutils/inetutils/libtelnet/auth.h,v
retrieving revision 1.4
diff -u -p -r1.4 auth.h
--- libtelnet/auth.h    6 Dec 2002 16:09:06 -0000       1.4
+++ libtelnet/auth.h    23 Jun 2003 14:49:47 -0000
@@ -75,6 +75,7 @@ typedef struct XauthP {
        void    (*reply) P((struct XauthP *, unsigned char *, int));
        int     (*status) P((struct XauthP *, char *, int));
        void    (*printsub) P((unsigned char *, int, unsigned char *, int));
+       void    (*cleanup) P((struct XauthP *));
 } TN_Authenticator;
 
 #include "auth-proto.h"
Index: libtelnet/shishi.c
===================================================================
RCS file: libtelnet/shishi.c
diff -N libtelnet/shishi.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ libtelnet/shishi.c  23 Jun 2003 14:49:47 -0000
@@ -0,0 +1,498 @@
+/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+
+This file is part of GNU Inetutils.
+
+GNU Inetutils is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Inetutils is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Inetutils; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef SHISHI
+#include <stdlib.h>
+#include <stdio.h>
+#include <arpa/telnet.h>
+#include <shishi.h>
+#include <assert.h>
+
+#include <netdb.h>
+#include <ctype.h>
+#include <syslog.h>
+#ifdef  HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#include "auth.h"
+#include "misc.h"
+
+static unsigned char str_data[2048] = { IAC, SB, TELOPT_AUTHENTICATION, 0,
+  AUTHTYPE_KERBEROS_V5,
+};
+
+#define KRB_AUTH             0 /* Authentication data follows */
+#define KRB_REJECT           1 /* Rejected (reason might follow) */
+#define KRB_ACCEPT           2 /* Accepted */
+#define KRB_RESPONSE         3 /* Response for mutual auth. */
+
+Shishi *shishi_handle = 0;
+Shishi_ap *auth_handle;
+
+#define DEBUG(c) if (auth_debug_mode) printf c
+
+static int
+Data (TN_Authenticator * ap, int type, unsigned char *d, int c)
+{
+  unsigned char *p = str_data + 4;
+  unsigned char *cd = (unsigned char *) d;
+
+  if (c == -1)
+    c = strlen (cd);
+
+  if (auth_debug_mode)
+    {
+      printf ("%s:%d: [%d] (%d)",
+             str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY",
+             str_data[3], type, c);
+      printd (d, c);
+      printf ("\r\n");
+    }
+
+  *p++ = ap->type;
+  *p++ = ap->way;
+  *p++ = type;
+
+  while (c-- > 0)
+    {
+      if ((*p++ = *cd++) == IAC)
+       *p++ = IAC;
+    }
+  *p++ = IAC;
+  *p++ = SE;
+  if (str_data[3] == TELQUAL_IS)
+    printsub ('>', &str_data[2], p - &str_data[2]);
+  return (net_write (str_data, p - str_data));
+}
+
+/* FIXME: Reverse return code! */
+int
+krb5shishi_init (TN_Authenticator * ap, int server)
+{
+  if (server)
+    {
+      str_data[3] = TELQUAL_REPLY;
+      if (!shishi_handle && shishi_init_server (&shishi_handle) != SHISHI_OK)
+       return 0;
+    }
+  else
+    {
+      str_data[3] = TELQUAL_IS;
+      if (!shishi_handle && shishi_init (&shishi_handle) != SHISHI_OK)
+       return 0;
+    }
+
+  return 1;
+}
+
+void
+krb5shishi_cleanup (TN_Authenticator * ap)
+{
+  if (shishi_handle == 0)
+    return;
+
+  shishi_done (shishi_handle);
+  shishi_handle = 0;
+}
+
+int
+krb5shishi_send (TN_Authenticator * ap)
+{
+  int ap_opts;
+  char type_check[2];
+  Shishi_tkt *tkt;
+  Shishi_tkts_hint hint;
+  int rc;
+  char *tmp;
+  char apreq[4096];
+  int apreq_len;
+
+  tmp = malloc (strlen ("host/") + strlen (RemoteHostName) + 1);
+  sprintf (tmp, "host/%s", RemoteHostName);
+  memset (&hint, 0, sizeof (hint));
+  hint.server = tmp;
+  hint.etype = SHISHI_DES_CBC_MD5;
+  tkt = shishi_tkts_get (shishi_tkts_default (shishi_handle), &hint);
+  free (tmp);
+  if (!tkt)
+    {
+      DEBUG (("telnet: Kerberos V5: no shishi ticket for server\r\n"));
+      return 0;
+    }
+
+  if (auth_debug_mode)
+    shishi_tkt_pretty_print (tkt, stdout);
+
+  if (!UserNameRequested)
+    {
+      DEBUG (("telnet: Kerberos V5: no user name supplied\r\n"));
+      return 0;
+    }
+
+  if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL)
+    ap_opts = SHISHI_APOPTIONS_MUTUAL_REQUIRED;
+  else
+    ap_opts = 0;
+
+  type_check[0] = ap->type;
+  type_check[1] = ap->way;
+
+  if (tkt)
+    {
+      rc = shishi_ap_tktoptionsdata (shishi_handle, &auth_handle, tkt,
+                                    ap_opts, (char *) &type_check, 2);
+      if (rc != SHISHI_OK)
+       {
+         DEBUG (("telnet: Kerberos V5: Could not make AP-REQ (%s)\r\n",
+                 shishi_strerror (rc)));
+         return 0;
+       }
+
+      apreq_len = sizeof (apreq);
+      rc = shishi_ap_req_der (auth_handle, apreq, &apreq_len);
+      if (rc != SHISHI_OK)
+       {
+         DEBUG (("telnet: Kerberos V5: could not DER encode (%s)\r\n",
+                 shishi_strerror (rc)));
+         return 0;
+       }
+
+      if (auth_debug_mode)
+       {
+         shishi_authenticator_print
+           (shishi_handle, stdout, shishi_ap_authenticator (auth_handle));
+         shishi_apreq_print (shishi_handle, stdout,
+                             shishi_ap_req (auth_handle));
+       }
+    }
+
+  if (!auth_sendname (UserNameRequested, strlen (UserNameRequested)))
+    {
+      DEBUG (("telnet: Not enough room for user name\r\n"));
+      return 0;
+    }
+
+  if (!Data (ap, KRB_AUTH, apreq, apreq_len))
+    {
+      DEBUG (("telnet: Not enough room for authentication data\r\n"));
+      return 0;
+    }
+
+  DEBUG (("telnet: Sent Kerberos V5 credentials to server\r\n"));
+
+  return 1;
+}
+
+void
+krb5shishi_reply (TN_Authenticator * ap, unsigned char *data, int cnt)
+{
+  static int mutual_complete = 0;
+
+  if (cnt-- < 1)
+    return;
+
+  switch (*data++)
+    {
+    case KRB_REJECT:
+      if (cnt > 0)
+       printf ("[ Kerberos V5 refuses authentication because %.*s ]\r\n",
+               cnt, data);
+      else
+       printf ("[ Kerberos V5 refuses authentication ]\r\n");
+      auth_send_retry ();
+      return;
+
+    case KRB_ACCEPT:
+      if (!mutual_complete)
+       {
+         if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL)
+           {
+             printf ("[ Kerberos V5 accepted you, "
+                     "but didn't provide mutual authentication! ]\r\n");
+             auth_send_retry ();
+             break;
+           }
+       }
+
+      if (cnt)
+       printf ("[ Kerberos V5 accepts you as ``%.*s''%s ]\r\n", cnt, data,
+               mutual_complete ?
+               " (server authenticated)" : " (server NOT authenticated)");
+      else
+       printf ("[ Kerberos V5 accepts you ]\r\n");
+      auth_finished (ap, AUTH_USER);
+      break;
+
+    case KRB_RESPONSE:
+      if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL)
+       {
+         if (shishi_ap_rep_verify_der (auth_handle, data, cnt) != SHISHI_OK)
+           {
+             printf ("[ Mutual authentication failed ]\r\n");
+             auth_send_retry ();
+             break;
+           }
+
+         if (auth_debug_mode)
+           {
+             shishi_aprep_print (shishi_handle, stdout,
+                                 shishi_ap_rep (auth_handle));
+             shishi_encapreppart_print (shishi_handle, stdout,
+                                        shishi_ap_encapreppart
+                                        (auth_handle));
+           }
+
+         mutual_complete = 1;
+       }
+      break;
+
+    default:
+      DEBUG (("Unknown Kerberos option %d\r\n", data[-1]));
+    }
+}
+
+int
+krb5shishi_status (TN_Authenticator * ap, char *name, int level)
+{
+  char cname[BUFSIZ];
+  int cnamelen;
+  int rc;
+
+  if (level < AUTH_USER)
+    return level;
+
+  cnamelen = sizeof (cname);
+  rc = shishi_encticketpart_cname_get
+    (shishi_handle, shishi_tkt_encticketpart (shishi_ap_tkt (auth_handle)),
+     cname, &cnamelen);
+
+  if (UserNameRequested && rc == SHISHI_OK &&
+      cnamelen == strlen (UserNameRequested) &&
+      memcmp (UserNameRequested, cname, cnamelen) == 0)
+    {
+      /* FIXME: Check buffer length */
+      strcpy (name, UserNameRequested);
+      return AUTH_VALID;
+    }
+
+  return AUTH_USER;
+}
+
+int
+krb5shishi_is_auth (TN_Authenticator * a, unsigned char *data, int cnt,
+                   char *errbuf, int errbuflen)
+{
+  Shishi_key *key, *key2;
+  int rc;
+  char cnamerealm[BUFSIZ];
+  int cnamerealmlen;
+
+  rc = shishi_ap (shishi_handle, &auth_handle);
+  if (rc != SHISHI_OK)
+    {
+      snprintf (errbuf, errbuflen,
+               "Cannot allocate authentication structures: %s",
+               shishi_strerror (rc));
+      return 1;
+    }
+
+  rc = shishi_ap_req_der_set (auth_handle, data, cnt);
+  if (rc != SHISHI_OK)
+    {
+      snprintf (errbuf, errbuflen,
+               "Cannot parse authentication information: %s",
+               shishi_strerror (rc));
+      return 1;
+    }
+
+  key = shishi_hostkeys_for_localservice (shishi_handle, "host");
+  if (key == NULL)
+    {
+      snprintf (errbuf, errbuflen, "Could not find key:\n%s\n",
+               shishi_strerror_details (shishi_handle));
+      return 1;
+    }
+
+  rc = shishi_ap_req_process (auth_handle, key);
+  if (rc != SHISHI_OK)
+    {
+      snprintf (errbuf, errbuflen, "Could not process AP-REQ: %s\n",
+               shishi_strerror (rc));
+      return 1;
+    }
+
+  if (shishi_apreq_mutual_required_p
+      (shishi_handle, shishi_ap_req (auth_handle)))
+    {
+      Shishi_asn1 aprep;
+      char der[BUFSIZ];
+      int derlen = BUFSIZ;;
+
+      rc = shishi_ap_rep_asn1 (auth_handle, &aprep);
+      if (rc != SHISHI_OK)
+       {
+         snprintf (errbuf, errbuflen, "Error creating AP-REP: %s\n",
+                   shishi_strerror (rc));
+         return 1;
+       }
+
+      rc = shishi_a2d (shishi_handle, aprep, der, &derlen);
+      if (rc != SHISHI_OK)
+       {
+         snprintf (errbuf, errbuflen, "Error der encoding aprep: %s\n",
+                   shishi_strerror (rc));
+         return 1;
+       }
+
+      Data (a, KRB_RESPONSE, der, derlen);
+    }
+
+  cnamerealmlen = sizeof (cnamerealm);
+  rc = shishi_encticketpart_cnamerealm_get
+    (shishi_handle, shishi_tkt_encticketpart (shishi_ap_tkt (auth_handle)),
+     cnamerealm, &cnamerealmlen);
+  if (rc != SHISHI_OK)
+    {
+      snprintf (errbuf, errbuflen, "Error getting authenticator name: %s\n",
+               shishi_strerror (rc));
+      return 1;
+    }
+  cnamerealm[cnamerealmlen] = '\0';
+
+  Data (a, KRB_ACCEPT, cnamerealm, cnamerealm ? -1 : 0);
+  DEBUG (("telnetd: Kerberos5 identifies him as ``%s''\r\n",
+         cnamerealm ? cnamerealm : ""));
+  auth_finished (a, AUTH_USER);
+
+  return 0;
+}
+
+void
+krb5shishi_is (TN_Authenticator * ap, unsigned char *data, int cnt)
+{
+  int r = 0;
+  char errbuf[512];
+
+  puts ("krb5shishi_is");
+
+  if (cnt-- < 1)
+    return;
+  errbuf[0] = 0;
+  switch (*data++)
+    {
+    case KRB_AUTH:
+      r = krb5shishi_is_auth (ap, data, cnt, errbuf, sizeof errbuf);
+      break;
+
+    default:
+      DEBUG (("Unknown Kerberos option %d\r\n", data[-1]));
+      Data (ap, KRB_REJECT, 0, 0);
+      break;
+    }
+
+  if (r)
+    {
+      if (!errbuf[0])
+       snprintf (errbuf, sizeof errbuf, "kerberos_is: error");
+      Data (ap, KRB_REJECT, errbuf, -1);
+      DEBUG (("%s\r\n", errbuf));
+      syslog (LOG_ERR, "%s", errbuf);
+    }
+}
+
+static char *
+req_type_str (int type)
+{
+  switch (type)
+    {
+    case KRB_REJECT:
+      return "REJECT";
+
+    case KRB_ACCEPT:
+      return "ACCEPT";
+
+    case KRB_AUTH:
+      return "AUTH";
+
+    case KRB_RESPONSE:
+      return "RESPONSE";
+
+    }
+  return NULL;
+}
+
+#define ADDC(p,l,c) if ((l) > 0) {*(p)++ = (c); --(l);}
+
+void
+krb5shishi_printsub (unsigned char *data, int cnt,
+                    unsigned char *buf, int buflen)
+{
+  char *p;
+  int i;
+
+  puts ("krb5shishi_printsub");
+
+  buf[buflen - 1] = '\0';      /* make sure its NULL terminated */
+  buflen -= 1;
+
+  p = req_type_str (data[3]);
+  if (!p)
+    {
+      int l = snprintf (buf, buflen, " %d (unknown)", data[3]);
+      buf += l;
+      buflen -= l;
+    }
+  else
+    {
+      while (buflen > 0 && (*buf++ = *p++) != 0)
+       buflen--;
+    }
+
+  switch (data[3])
+    {
+    case KRB_REJECT:           /* Rejected (reason might follow) */
+    case KRB_ACCEPT:           /* Accepted (username might follow) */
+      if (cnt <= 4)
+       break;
+      ADDC (buf, buflen, '"');
+      for (i = 4; i < cnt; i++)
+       ADDC (buf, buflen, data[i]);
+      ADDC (buf, buflen, '"');
+      ADDC (buf, buflen, '\0');
+      break;
+
+    case KRB_AUTH:
+    case KRB_RESPONSE:
+      for (i = 4; buflen > 0 && i < cnt; i++)
+       {
+         int l = snprintf (buf, buflen, " %d", data[i]);
+         buf += l;
+         buflen -= l;
+       }
+    }
+}
+
+#endif /* SHISHI */





reply via email to

[Prev in Thread] Current Thread [Next in Thread]