Mon Mar 18 20:56:37 1996 Theodore Y. Ts'o <tytso@dcl>
authorTheodore Tso <tytso@mit.edu>
Tue, 19 Mar 1996 02:33:21 +0000 (02:33 +0000)
committerTheodore Tso <tytso@mit.edu>
Tue, 19 Mar 1996 02:33:21 +0000 (02:33 +0000)
      * kerberos5.c (kerberos5_send): Send in as input the
              authentication type pair (ap->type, ap->way) to be
              checksumed in the authenticator.
              (kerberos5_is): If the checksum is present in the
              authenticator, then validate the authentication type pair
              against the checksum.
              (kerberos5_reply): If we didn't do mutual authentication,
              and we receive a KRB_ACCEPT, then stash away the session
              key anyway.  This way we have a chance of doing encryption
              even if mutual authentication wasn't done.

      * encrypt.c (EncryptStartInput, EncryptStartOutput): Added
              conditional around printf so that these two functions can
              be called by the server.
              (encrypt_is_encrypting): New function which returns true
              only if both sides of the telnet stream is encrypted.

Fri Mar 15 18:19:44 1996  Theodore Y. Ts'o  <tytso@dcl>

      * auth.c: Added new authentication scheme for Krb5 mutual
              authentication with mandatory encryption.
              (auth_send, auth_send_retry): Split auth_send() so that
              the functionality done by auth_send_retry() is separate.
              This avoids a really dodgy pointer comparison which was
              caused by auth_send() being used for two purposes.
              If the client has not requested encryption, then don't
              use the authentication systems which require encryption.
              (auth_must_encrypt):  New function which returns whether
              or not encryption must be negotiated.

      * auth-proto.h: Added prototype for new option
              auth_must_encrypt().

      * Makefile.in (ENCRYPTION, DES_ENCRYPTION): Added defines to turn
              on encryption and des encryption.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@7661 dc483132-0cff-0310-8789-dd5450dbe970

src/appl/telnet/libtelnet/ChangeLog
src/appl/telnet/libtelnet/Makefile.in
src/appl/telnet/libtelnet/auth-proto.h
src/appl/telnet/libtelnet/auth.c
src/appl/telnet/libtelnet/enc-proto.h
src/appl/telnet/libtelnet/encrypt.c
src/appl/telnet/libtelnet/kerberos5.c

index eca0bd655e24d8a881024494fc512b34e8cff3c0..391cd7166dd7374f12fd4279271af7fa1c25ad8e 100644 (file)
@@ -1,3 +1,41 @@
+Mon Mar 18 20:56:37 1996  Theodore Y. Ts'o  <tytso@dcl>
+
+       * kerberos5.c (kerberos5_send): Send in as input the
+               authentication type pair (ap->type, ap->way) to be
+               checksumed in the authenticator.
+               (kerberos5_is): If the checksum is present in the
+               authenticator, then validate the authentication type pair
+               against the checksum.
+               (kerberos5_reply): If we didn't do mutual authentication,
+               and we receive a KRB_ACCEPT, then stash away the session
+               key anyway.  This way we have a chance of doing encryption
+               even if mutual authentication wasn't done.
+
+       * encrypt.c (EncryptStartInput, EncryptStartOutput): Added
+               conditional around printf so that these two functions can
+               be called by the server.
+               (encrypt_is_encrypting): New function which returns true
+               only if both sides of the telnet stream is encrypted.
+
+Fri Mar 15 18:19:44 1996  Theodore Y. Ts'o  <tytso@dcl>
+
+       * auth.c: Added new authentication scheme for Krb5 mutual
+               authentication with mandatory encryption.
+               (auth_send, auth_send_retry): Split auth_send() so that
+               the functionality done by auth_send_retry() is separate.
+               This avoids a really dodgy pointer comparison which was
+               caused by auth_send() being used for two purposes.  
+               If the client has not requested encryption, then don't
+               use the authentication systems which require encryption.
+               (auth_must_encrypt):  New function which returns whether 
+               or not encryption must be negotiated.
+
+       * auth-proto.h: Added prototype for new option
+               auth_must_encrypt().
+
+       * Makefile.in (ENCRYPTION, DES_ENCRYPTION): Added defines to turn
+               on encryption and des encryption.
+
 Fri Jan 26 01:05:46 1996  Sam Hartman  <hartmans@tertius.mit.edu>
 
        * kerberos5.c (kerberos5_send): Get DES_CBC-CRC credentials.
index bc9d47abae88163c49de537d23a508ea22a61886..d0af781c89cb6a3f4b07b3be876085e855f2eb68 100644 (file)
@@ -19,7 +19,8 @@
 #
 #      @(#)Makefile.generic    5.5 (Berkeley) 3/1/91
 #
-AUTH_DEF=-DAUTHENTICATION -DKRB5 -DFORWARD -UNO_LOGIN_F -DLOGIN_CAP_F -DLOGIN_PROGRAM=KRB5_PATH_LOGIN
+AUTH_DEF=-DAUTHENTICATION -DENCRYPTION -DDES_ENCRYPTION -DKRB5 -DFORWARD \
+       -UNO_LOGIN_F -DLOGIN_CAP_F -DLOGIN_PROGRAM=KRB5_PATH_LOGIN
 LOCALINCLUDES=-I.. -I$(srcdir)/.. -I$(SRCTOP)/include/kerberosIV
 CFLAGS = $(CCOPTS) $(AUTH_DEF) $(DEFS) $(LOCALINCLUDES)
 LIBOBJS=@LIBOBJS@
index b93c9c266bc572c738cd7ea0425b721b96eb31fe..c14f6ed0648968f53ba8f218b5c581d15b4c0682 100644 (file)
@@ -73,6 +73,7 @@ void auth_is P((unsigned char *, int));
 void auth_reply P((unsigned char *, int));
 void auth_finished P((Authenticator *, int));
 int auth_wait P((char *));
+int auth_must_encrypt P((void));
 void auth_disable_name P((char *));
 void auth_gen_printsub P((unsigned char *, int, unsigned char *, int));
 
index dc54fb07a15318d081e34502f85da910950c9638..50a6ae79faea4fd5de2db47e7ec8a68f7d8b06af 100644 (file)
@@ -95,6 +95,8 @@ extern rsaencpwd_printsub();
 #endif
 
 int auth_debug_mode = 0;
+int auth_has_failed = 0;
+int auth_enable_encrypt = 0;
 static         char    *Name = "Noname";
 static int     Server = 0;
 static Authenticator   *authenticated = 0;
@@ -126,6 +128,16 @@ Authenticator authenticators[] = {
                                spx_printsub },
 #endif
 #ifdef KRB5
+#ifdef ENCRYPTION
+       { AUTHTYPE_KERBEROS_V5,
+                 AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL|AUTH_ENCRYPT_ON,
+                               kerberos5_init,
+                               kerberos5_send,
+                               kerberos5_is,
+                               kerberos5_reply,
+                               kerberos5_status,
+                               kerberos5_printsub },
+#endif 
        { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
                                kerberos5_init,
                                kerberos5_send,
@@ -387,10 +399,6 @@ auth_send(data, cnt)
        unsigned char *data;
        int cnt;
 {
-       Authenticator *ap;
-       static unsigned char str_none[] = { IAC, SB, TELOPT_AUTHENTICATION,
-                                           TELQUAL_IS, AUTHTYPE_NULL, 0,
-                                           IAC, SE };
        if (Server) {
                if (auth_debug_mode) {
                        printf(">>>%s: auth_send called!\r\n", Name);
@@ -404,63 +412,68 @@ auth_send(data, cnt)
        }
 
        /*
-        * Save the data, if it is new, so that we can continue looking
-        * at it if the authorization we try doesn't work
+        * Save the list of authentication mechanisms
         */
-       /* ANSI X3.159-1989 section 3.3.6 indicates that this entire 
-          conditional is undefined. It will probably work on flat address
-          space UNIX systems though. --eichin@mit.edu */
-       if (data < _auth_send_data ||
-           data > _auth_send_data + sizeof(_auth_send_data)) {
-               auth_send_cnt = cnt > sizeof(_auth_send_data)
-                                       ? sizeof(_auth_send_data)
-                                       : cnt;
-               memcpy((void *)_auth_send_data, (void *)data, auth_send_cnt);
-               auth_send_data = _auth_send_data;
-       } else {
+       auth_send_cnt = cnt;
+       if (auth_send_cnt > sizeof(_auth_send_data))
+           auth_send_cnt = sizeof(_auth_send_data);
+       memcpy((void *)_auth_send_data, (void *)data, auth_send_cnt);
+       auth_send_data = _auth_send_data;
+
+       auth_send_retry();
+}
+
+/*
+ * Try the next authentication mechanism on the list, and see if it
+ * works.
+ */
+void auth_send_retry()
+{
+       Authenticator *ap;
+       static unsigned char str_none[] = { IAC, SB, TELOPT_AUTHENTICATION,
+                                           TELQUAL_IS, AUTHTYPE_NULL, 0,
+                                           IAC, SE };
+       
+       if (Server) {
+               if (auth_debug_mode) {
+                       printf(">>>%s: auth_send_retry called!\r\n", Name);
+               }
+               return;
+       }
+
+       for (;(auth_send_cnt -= 2) >= 0; auth_send_data += 2) {
+           if (auth_debug_mode)
+               printf(">>>%s: He supports %d\r\n", Name, *auth_send_data);
+           if (!(i_support & typemask(*auth_send_data)))
+               continue;
+           if (i_wont_support & typemask(*auth_send_data))
+               continue;
+           ap = findauthenticator(auth_send_data[0], auth_send_data[1]);
+           if (!ap || !ap->send)
+               continue;
+           if ((ap->way & AUTH_ENCRYPT_MASK) && !auth_enable_encrypt)
+               continue;
+
+           if (auth_debug_mode)
+               printf(">>>%s: Trying %d %d\r\n", Name, auth_send_data[0],
+                      auth_send_data[1]);
+           if ((*ap->send)(ap)) {
                /*
-                * This is probably a no-op, but we just make sure
+                * Okay, we found one we like and did it.  we can go
+                * home now.
                 */
-               auth_send_data = data;
-               auth_send_cnt = cnt;
-       }
-       while ((auth_send_cnt -= 2) >= 0) {
                if (auth_debug_mode)
-                       printf(">>>%s: He supports %d\r\n",
-                               Name, *auth_send_data);
-               if ((i_support & ~i_wont_support) & typemask(*auth_send_data)) {
-                       ap = findauthenticator(auth_send_data[0],
-                                              auth_send_data[1]);
-                       if (ap && ap->send) {
-                               if (auth_debug_mode)
-                                       printf(">>>%s: Trying %d %d\r\n",
-                                               Name, auth_send_data[0],
-                                                       auth_send_data[1]);
-                               if ((*ap->send)(ap)) {
-                                       /*
-                                        * Okay, we found one we like
-                                        * and did it.
-                                        * we can go home now.
-                                        */
-                                       if (auth_debug_mode)
-                                               printf(">>>%s: Using type %d\r\n",
-                                                       Name, *auth_send_data);
-                                       auth_send_data += 2;
-                                       return;
-                               }
-                       }
-                       /* else
-                        *      just continue on and look for the
-                        *      next one if we didn't do anything.
-                        */
-               }
+                   printf(">>>%s: Using type %d\r\n", Name, *auth_send_data);
                auth_send_data += 2;
+               return;
+           }
        }
        net_write(str_none, sizeof(str_none));
        printsub('>', &str_none[2], sizeof(str_none) - 2);
        if (auth_debug_mode)
                printf(">>>%s: Sent failure message\r\n", Name);
        auth_finished(0, AUTH_REJECT);
+       auth_has_failed = 1;
 #ifdef KANNAN
        /*
         *  We requested strong authentication, however no mechanisms worked.
@@ -471,16 +484,6 @@ auth_send(data, cnt)
 #endif /* KANNAN */
 }
 
-       void
-auth_send_retry()
-{
-       /*
-        * if auth_send_cnt <= 0 then auth_send will end up rejecting
-        * the authentication and informing the other side of this.
-        */
-       auth_send(auth_send_data, auth_send_cnt);
-}
-
        void
 auth_is(data, cnt)
        unsigned char *data;
@@ -621,6 +624,14 @@ auth_wait(name)
        return(validuser);
 }
 
+int auth_must_encrypt()
+{
+    if (authenticated &&
+       ((authenticated->way & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON))
+       return 1;
+    return 0;
+}
+
        void
 auth_debug(mode)
        int mode;
index d5f31d6072773150a14a64acae9a85a408691e9b..996a4f5d06b63d96233261080bae5751ecacaf68 100644 (file)
@@ -77,6 +77,7 @@ void encrypt_send_request_start P((void));
 void encrypt_send_request_end P((void));
 void encrypt_send_end P((void));
 void encrypt_wait P((void));
+int encrypt_is_encrypting P((void));
 void encrypt_send_support P((void));
 void encrypt_send_keyid P((int, unsigned char *, int, int));
 int net_write P((unsigned char *, int));
index 4a539b92be80e171a85464533b562c4b8ecd8067..15f2a88b2616c6174d84e7d9414d8f40c836b3e7 100644 (file)
@@ -332,7 +332,8 @@ EncryptStartInput()
                encrypt_send_request_start();
                return(1);
        }
-       printf("No previous decryption mode, decryption not enabled\r\n");
+       if (!Server)
+           printf("No previous decryption mode, decryption not enabled\r\n");
        return(0);
 }
 
@@ -343,7 +344,8 @@ EncryptStartOutput()
                encrypt_start_output(encrypt_mode);
                return(1);
        }
-       printf("No previous encryption mode, encryption not enabled\r\n");
+       if (!Server)
+           printf("No previous encryption mode, encryption not enabled\r\n");
        return(0);
 }
 
@@ -723,27 +725,28 @@ encrypt_request_start(data, cnt)
 
 static unsigned char str_keyid[(MAXKEYLEN*2)+5] = { IAC, SB, TELOPT_ENCRYPT };
 
-encrypt_enc_keyid(keyid, len)
+void encrypt_keyid();
+               
+void encrypt_enc_keyid(keyid, len)
        unsigned char *keyid;
        int len;
 {
        encrypt_keyid(&ki[1], keyid, len);
 }
 
-encrypt_dec_keyid(keyid, len)
+void encrypt_dec_keyid(keyid, len)
        unsigned char *keyid;
        int len;
 {
        encrypt_keyid(&ki[0], keyid, len);
 }
 
-encrypt_keyid(kp, keyid, len)
+void encrypt_keyid(kp, keyid, len)
        struct key_info *kp;
        unsigned char *keyid;
        int len;
 {
        Encryptions *ep;
-       unsigned char *strp, *cp;
        int dir = kp->dir;
        register int ret = 0;
 
@@ -939,7 +942,6 @@ encrypt_send_request_end()
        void
 encrypt_wait()
 {
-       register int encrypt, decrypt;
        if (encrypt_debug_mode)
                printf(">>>%s: in encrypt_wait\r\n", Name);
        if (!havesessionkey || !(I_SUPPORT_ENCRYPT & remote_supports_decrypt))
@@ -949,6 +951,13 @@ encrypt_wait()
                        return;
 }
 
+int encrypt_is_encrypting()
+{
+    if (encrypt_output && decrypt_input)
+       return 1;
+    return 0;
+}
+
        void
 encrypt_debug(mode)
        int mode;
index 523657a22cba09fca82d4c53777fbe692eb2e7d6..6f947e3579589b104c729b475d795769ef8134f2 100644 (file)
@@ -188,6 +188,8 @@ kerberos5_send(ap)
        krb5_creds creds;               /* telnet gets session key from here */
        krb5_creds * new_creds = 0;
        int ap_opts;
+       char type_check[2];
+       krb5_data check_data;
 
 #ifdef ENCRYPTION
        krb5_keyblock *newkey = 0;
@@ -259,34 +261,47 @@ kerberos5_send(ap)
        ap_opts |= AP_OPTS_USE_SUBKEY;
 #endif /* ENCRYPTION */
            
-    if ((r = krb5_auth_con_init(telnet_context, &auth_context))) {
-       if (auth_debug_mode) {
-           printf("Kerberos V5: failed to init auth_context (%s)\r\n",
-                  error_message(r));
+       if (auth_context) {
+           krb5_auth_con_free(telnet_context, auth_context);
+           auth_context = 0;
        }
-       return(0);
-    }
-    
-    krb5_auth_con_setflags(telnet_context, auth_context,
-                           KRB5_AUTH_CONTEXT_RET_TIME);
-
-    r = krb5_mk_req_extended(telnet_context, &auth_context, ap_opts,
-                            NULL, new_creds, &auth);
+       if ((r = krb5_auth_con_init(telnet_context, &auth_context))) {
+           if (auth_debug_mode) {
+               printf("Kerberos V5: failed to init auth_context (%s)\r\n",
+                      error_message(r));
+           }
+           return(0);
+       }
+       
+       krb5_auth_con_setflags(telnet_context, auth_context,
+                              KRB5_AUTH_CONTEXT_RET_TIME);
+       
+       type_check[0] = ap->type;
+       type_check[1] = ap->way;
+       check_data.magic = KV5M_DATA;
+       check_data.length = 2;
+       check_data.data = (char *) &type_check;
+
+       r = krb5_mk_req_extended(telnet_context, &auth_context, ap_opts,
+                                &check_data, new_creds, &auth);
 
 #ifdef ENCRYPTION
        krb5_auth_con_getlocalsubkey(telnet_context, auth_context, &newkey);
        if (session_key) {
-krb5_free_keyblock(telnet_context, session_key);
-session_key = 0;
+               krb5_free_keyblock(telnet_context, session_key);
+               session_key = 0;
        }
 
        if (newkey) {
            /* keep the key in our private storage, but don't use it
               yet---see kerberos5_reply() below */
-           if ((newkey->enctype != ENCTYPE_DES_CBC_CRC) && (newkey-> enctype != ENCTYPE_DES_CBC_MD5)) {
-               if ((new_creds->keyblock.enctype == ENCTYPE_DES_CBC_CRC)||( new_creds->keyblock.enctype == ENCTYPE_DES_CBC_MD5))
+           if ((newkey->enctype != ENCTYPE_DES_CBC_CRC) &&
+               (newkey-> enctype != ENCTYPE_DES_CBC_MD5)) {
+               if ((new_creds->keyblock.enctype == ENCTYPE_DES_CBC_CRC) ||
+                   (new_creds->keyblock.enctype == ENCTYPE_DES_CBC_MD5))
                    /* use the session key in credentials instead */
-                   krb5_copy_keyblock(telnet_context,&new_creds->keyblock, &session_key);
+                   krb5_copy_keyblock(telnet_context,&new_creds->keyblock,
+                                      &session_key);
                else
                    /* XXX ? */;
            } else {
@@ -334,9 +349,11 @@ kerberos5_is(ap, data, cnt)
 #ifdef ENCRYPTION
        Session_Key skey;
 #endif
+       char errbuf[128];
        char *name;
        char *getenv();
        krb5_data inbuf;
+       krb5_authenticator *authenticator;
 
        if (cnt-- < 1)
                return;
@@ -370,21 +387,61 @@ kerberos5_is(ap, data, cnt)
                    krb5_free_principal(telnet_context, server);
                }
                if (r) {
-                       char errbuf[128];
-
-                   errout:
-                       (void) strcpy(errbuf, "Read req failed: ");
+                       (void) strcpy(errbuf, "krb5_rd_req failed: ");
                        (void) strcat(errbuf, error_message(r));
-                       Data(ap, KRB_REJECT, errbuf, -1);
-                       if (auth_debug_mode)
-                               printf("%s\r\n", errbuf);
-                       return;
+                       goto errout;
+               }
+               r = krb5_auth_con_getauthenticator(telnet_context,
+                                                  auth_context,
+                                                  &authenticator);
+               if (r) {
+                   (void) strcpy(errbuf,
+                                 "krb5_auth_con_getauthenticator failed: ");
+                   (void) strcat(errbuf, error_message(r));
+                   goto errout;
+               }
+               if ((ap->way & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON &&
+                   !authenticator->checksum) {
+                       (void) strcpy(errbuf,
+                               "authenticator is missing required checksum");
+                       goto errout;
+               }
+               if (authenticator->checksum) {
+                   char type_check[2];
+                   krb5_checksum *cksum = authenticator->checksum;
+                   krb5_keyblock *key;
+
+                   type_check[0] = ap->type;
+                   type_check[1] = ap->way;
+
+                   r = krb5_auth_con_getkey(telnet_context, auth_context,
+                                            &key);
+                   if (r) {
+                       (void) strcpy(errbuf, "krb5_auth_con_getkey failed: ");
+                       (void) strcat(errbuf, error_message(r));
+                       goto errout;
+                   }
+                   r = krb5_verify_checksum(telnet_checksum,
+                                            cksum->checksum_type, cksum,
+                                            &type_check, 2, key->contents,
+                                            key->length);
+                   if (r) {
+                       (void) strcpy(errbuf,
+                                     "checksum verification failed: ");
+                       (void) strcat(errbuf, error_message(r));
+                       goto errout;
+                   }
+                   krb5_free_keyblock(telnet_context, key);
                }
+               krb5_free_authenticator(telnet_context, authenticator);
                if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
                    /* do ap_rep stuff here */
                    if ((r = krb5_mk_rep(telnet_context, auth_context,
-                                        &outbuf)))
+                                        &outbuf))) {
+                       (void) strcpy(errbuf, "Make reply failed: ");
+                       (void) strcat(errbuf, error_message(r));
                        goto errout;
+                   }
 
                    Data(ap, KRB_RESPONSE, outbuf.data, outbuf.length);
                } 
@@ -403,23 +460,17 @@ kerberos5_is(ap, data, cnt)
                    free(name);
                krb5_auth_con_getremotesubkey(telnet_context, auth_context,
                                              &newkey);
+               if (session_key) {
+                   krb5_free_keyblock(telnet_context, session_key);
+                   session_key = 0;
+               }
                if (newkey) {
-                   if (session_key) {
-                       krb5_free_keyblock(telnet_context, session_key);
-                       session_key = 0;
-                   }
-
-                   krb5_copy_keyblock(telnet_context, newkey,
-                                               &session_key);
+                   krb5_copy_keyblock(telnet_context, newkey, &session_key);
                    krb5_free_keyblock(telnet_context, newkey);
                } else {
-                   if (session_key){
-                       krb5_free_keyblock(telnet_context, session_key);
-session_key = 0;
-                   }
-                   krb5_copy_keyblock(telnet_context, 
-                                          ticket->enc_part2->session,
-                                          &session_key);
+                   krb5_copy_keyblock(telnet_context,
+                                      ticket->enc_part2->session,
+                                      &session_key);
                }
                
 #ifdef ENCRYPTION
@@ -458,6 +509,17 @@ session_key = 0;
                Data(ap, KRB_REJECT, 0, 0);
                break;
        }
+       return;
+       
+    errout:
+       Data(ap, KRB_REJECT, errbuf, -1);
+       if (auth_debug_mode)
+           printf("%s\r\n", errbuf);
+       if (auth_context) {
+           krb5_auth_con_free(telnet_context, auth_context);
+           auth_context = 0;
+       }
+       return;
 }
 
        void
@@ -483,11 +545,20 @@ kerberos5_reply(ap, data, cnt)
                auth_send_retry();
                return;
        case KRB_ACCEPT:
-               if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL &&
-                   !mutual_complete) {
-                   printf("[ Kerberos V5 accepted you, but didn't provide mutual authentication! ]\r\n");
-                   auth_send_retry();
-                   return;
+               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();
+                       return;
+                   }
+#ifdef ENCRYPTION
+                   if (session_key) {
+                       skey.type = SK_DES;
+                       skey.length = 8;
+                       skey.data = session_key->contents;
+                       encrypt_session_key(&skey, 0);
+                   }
+#endif /* ENCRYPTION */
                }
                if (cnt)
                    printf("[ Kerberos V5 accepts you as ``%.*s'' ]\r\n", cnt, data);
@@ -542,6 +613,7 @@ kerberos5_reply(ap, data, cnt)
                        printf("Unknown Kerberos option %d\r\n", data[-1]);
                return;
        }
+       return;
 }
 
        int