 6b45c3512e
			
		
	
	6b45c3512e
	
	
	
		
			
			The online LIST interrupt message is a NOP, but it's expected to not have a reply (the server doesn't send one if it receives it before the LIST finishes). However, if the interrupt message arrives after the LIST finished, then it does get a reply, and this causes the client to get out of step with the server. Fixes include: 1) flavor the interrupt NOP to make sure it never gets a reply, 2) introduce a new kadm_list_interrtupt message that is like a NOP that produces no reply 3) always consume -after the LIST ends- a reply to any list interrupt NOP on the client side. This implements (1).
		
			
				
	
	
		
			304 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			304 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 1997 - 1999 Kungliga Tekniska Högskolan
 | |
|  * (Royal Institute of Technology, Stockholm, Sweden).
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  *
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  *
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer in the
 | |
|  *    documentation and/or other materials provided with the distribution.
 | |
|  *
 | |
|  * 3. Neither the name of the Institute nor the names of its contributors
 | |
|  *    may be used to endorse or promote products derived from this software
 | |
|  *    without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 | |
|  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | |
|  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 | |
|  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | |
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | |
|  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | |
|  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | |
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | |
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|  * SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| #include "kadm5_locl.h"
 | |
| 
 | |
| RCSID("$Id$");
 | |
| 
 | |
| kadm5_ret_t
 | |
| kadm5_c_get_principals(void *server_handle,
 | |
| 		       const char *expression,
 | |
| 		       char ***princs,
 | |
| 		       int *count)
 | |
| {
 | |
|     kadm5_client_context *context = server_handle;
 | |
|     kadm5_ret_t ret;
 | |
|     krb5_storage *sp;
 | |
|     unsigned char buf[1024];
 | |
|     int32_t tmp;
 | |
|     krb5_data reply;
 | |
|     int i;
 | |
| 
 | |
|     *count = 0;
 | |
|     *princs = NULL;
 | |
| 
 | |
|     ret = _kadm5_connect(server_handle, 0 /* want_write */);
 | |
|     if (ret)
 | |
| 	return ret;
 | |
| 
 | |
|     krb5_data_zero(&reply);
 | |
| 
 | |
|     sp = krb5_storage_from_mem(buf, sizeof(buf));
 | |
|     if (sp == NULL) {
 | |
| 	ret = krb5_enomem(context->context);
 | |
| 	goto out_keep_error;
 | |
|     }
 | |
|     ret = krb5_store_int32(sp, kadm_get_princs);
 | |
|     if (ret)
 | |
| 	goto out;
 | |
|     ret = krb5_store_int32(sp, expression != NULL ? 1 : 0);
 | |
|     if (ret)
 | |
| 	goto out;
 | |
|     if (expression) {
 | |
| 	ret = krb5_store_string(sp, expression);
 | |
| 	if (ret)
 | |
| 	    goto out;
 | |
|     }
 | |
|     ret = _kadm5_client_send(context, sp);
 | |
|     if (ret)
 | |
| 	goto out_keep_error;
 | |
|     ret = _kadm5_client_recv(context, &reply);
 | |
|     if (ret)
 | |
| 	goto out_keep_error;
 | |
|     krb5_storage_free(sp);
 | |
|     sp = krb5_storage_from_data (&reply);
 | |
|     if (sp == NULL) {
 | |
| 	ret = krb5_enomem(context->context);
 | |
| 	goto out_keep_error;
 | |
|     }
 | |
|     ret = krb5_ret_int32(sp, &tmp);
 | |
|     if (ret == 0)
 | |
| 	ret = tmp;
 | |
| 
 | |
|     if (ret)
 | |
| 	goto out;
 | |
| 
 | |
|     ret = krb5_ret_int32(sp, &tmp);
 | |
|     if (ret)
 | |
| 	goto out;
 | |
| 
 | |
|     *princs = calloc(tmp + 1, sizeof(**princs));
 | |
|     if (*princs == NULL) {
 | |
| 	ret = krb5_enomem(context->context);
 | |
| 	goto out_keep_error;
 | |
|     }
 | |
|     for (i = 0; i < tmp; i++) {
 | |
| 	ret = krb5_ret_string(sp, &(*princs)[i]);
 | |
| 	if (ret)
 | |
| 	    goto out;
 | |
|     }
 | |
|     *count = tmp;
 | |
| 
 | |
|   out:
 | |
|     krb5_clear_error_message(context->context);
 | |
| 
 | |
|   out_keep_error:
 | |
|     krb5_storage_free(sp);
 | |
|     krb5_data_free(&reply);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| kadm5_ret_t
 | |
| kadm5_c_iter_principals(void *server_handle,
 | |
| 			const char *expression,
 | |
| 			int (*cb)(void *, const char *),
 | |
| 			void *cbdata)
 | |
| {
 | |
|     kadm5_client_context *context = server_handle;
 | |
|     kadm5_ret_t ret;
 | |
|     krb5_storage *sp;
 | |
|     unsigned char buf[1024];
 | |
|     int32_t tmp;
 | |
|     krb5_data reply;
 | |
|     size_t i;
 | |
| 
 | |
|     ret = _kadm5_connect(server_handle, 0 /* want_write */);
 | |
|     if (ret)
 | |
| 	return ret;
 | |
| 
 | |
|     krb5_data_zero(&reply);
 | |
| 
 | |
|     sp = krb5_storage_from_mem(buf, sizeof(buf));
 | |
|     if (sp == NULL) {
 | |
| 	ret = krb5_enomem(context->context);
 | |
| 	goto out_keep_error;
 | |
|     }
 | |
|     ret = krb5_store_int32(sp, kadm_get_princs);
 | |
|     if (ret)
 | |
| 	goto out;
 | |
| 
 | |
|     /*
 | |
|      * Our protocol has an int boolean for this operation to indicate whether
 | |
|      * there's an expression.  What we'll do here is that instead of sending
 | |
|      * just false or trueish, for online iteration we'll send a number other
 | |
|      * than 0 or 1 -- a magic value > 0 and < INT_MAX.
 | |
|      *
 | |
|      * In response we'll expect multiple replies, each with up to some small
 | |
|      * number of principal names.  See kadmin/server.c.
 | |
|      */
 | |
|     ret = krb5_store_int32(sp, 0x55555555);
 | |
|     if (ret)
 | |
| 	goto out;
 | |
|     ret = krb5_store_string(sp, expression ? expression : "");
 | |
|     if (ret)
 | |
|         goto out;
 | |
|     ret = _kadm5_client_send(context, sp);
 | |
|     if (ret)
 | |
| 	goto out_keep_error;
 | |
|     ret = _kadm5_client_recv(context, &reply);
 | |
|     if (ret)
 | |
| 	goto out_keep_error;
 | |
|     krb5_storage_free(sp);
 | |
|     sp = krb5_storage_from_data (&reply);
 | |
|     if (sp == NULL) {
 | |
| 	ret = krb5_enomem(context->context);
 | |
| 	goto out_keep_error;
 | |
|     }
 | |
|     ret = krb5_ret_int32(sp, &tmp);
 | |
|     if (ret == 0)
 | |
| 	ret = tmp;
 | |
| 
 | |
|     if (ret)
 | |
| 	goto out;
 | |
| 
 | |
|     ret = krb5_ret_int32(sp, &tmp);
 | |
|     if (ret)
 | |
| 	goto out;
 | |
| 
 | |
|     if (tmp < 0) {
 | |
|         size_t n = -tmp;
 | |
|         int more = 1;
 | |
|         int stop = 0;
 | |
| 
 | |
|         /* The server supports online iteration, hooray! */
 | |
| 
 | |
|         while (more) {
 | |
|             /*
 | |
|              * We expect any number of chunks, each having `n' names, except
 | |
|              * the last one would have fewer than `n' (possibly zero, even).
 | |
|              *
 | |
|              * After that we expect one more reply with just a final return
 | |
|              * code.
 | |
|              */
 | |
|             krb5_data_free(&reply);
 | |
|             krb5_storage_free(sp);
 | |
|             sp = NULL;
 | |
|             ret = _kadm5_client_recv(context, &reply);
 | |
|             if (ret == 0 && (sp = krb5_storage_from_data(&reply)) == NULL)
 | |
|                 ret = krb5_enomem(context->context);
 | |
|             if (ret)
 | |
|                 goto out;
 | |
| 
 | |
|             /* Every chunk begins with a status code */
 | |
|             ret = krb5_ret_int32(sp, &tmp);
 | |
|             if (ret == 0)
 | |
|                 ret = tmp;
 | |
|             if (ret)
 | |
|                 goto out;
 | |
| 
 | |
|             /* We expect up to -tmp principals per reply */
 | |
|             for (i = 0; i < n; i++) {
 | |
|                 char *princ = NULL;
 | |
| 
 | |
|                 ret = krb5_ret_string(sp, &princ);
 | |
|                 if (ret == HEIM_ERR_EOF) {
 | |
|                     /* This was the last reply */
 | |
|                     more = 0;
 | |
|                     ret = 0;
 | |
|                     break;
 | |
|                 }
 | |
|                 if (ret)
 | |
|                     goto out;
 | |
|                 if (!stop) {
 | |
|                     stop = cb(cbdata, princ);
 | |
|                     if (stop) {
 | |
|                         /*
 | |
|                          * Tell the server to stop.
 | |
|                          *
 | |
|                          * We use a NOP for this, but with a payload that says
 | |
|                          * "don't reply to the NOP" just in case the NOP
 | |
|                          * arrives and is processed _after_ the LISTing has
 | |
|                          * finished.
 | |
|                          */
 | |
|                         krb5_storage_free(sp);
 | |
|                         if ((sp = krb5_storage_emem()) &&
 | |
|                             krb5_store_int32(sp, kadm_nop) == 0 &&
 | |
|                             krb5_store_int32(sp, 0))
 | |
|                             (void) _kadm5_client_send(context, sp);
 | |
|                     }
 | |
|                 }
 | |
|                 free(princ);
 | |
|             }
 | |
| 
 | |
|             if (!more) {
 | |
|                 if (ret == 0)
 | |
|                     ret = stop;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         /* Get the final result code */
 | |
|         krb5_data_free(&reply);
 | |
|         krb5_storage_free(sp);
 | |
|         sp = NULL;
 | |
|         ret = _kadm5_client_recv(context, &reply);
 | |
|         if (ret == 0 && (sp = krb5_storage_from_data(&reply)) == NULL)
 | |
|             ret = krb5_enomem(context->context);
 | |
|         if (ret)
 | |
|             goto out;
 | |
|         ret = krb5_ret_int32(sp, &tmp);
 | |
|         if (ret == 0)
 | |
|             ret = tmp;
 | |
|         if (!stop) {
 | |
|             /*
 | |
|              * Send our "interrupt" after the last chunk if we hand't
 | |
|              * interrupted already.
 | |
|              */
 | |
|             krb5_storage_free(sp);
 | |
|             if ((sp = krb5_storage_emem()) &&
 | |
|                 krb5_store_int32(sp, kadm_nop) == 0)
 | |
|                 (void) _kadm5_client_send(context, sp);
 | |
|         }
 | |
|     } else {
 | |
|         size_t n = tmp;
 | |
| 
 | |
|         /* Old server -- listing not online */
 | |
|         for (i = 0; i < n; i++) {
 | |
|             char *princ = NULL;
 | |
| 
 | |
|             ret = krb5_ret_string(sp, &princ);
 | |
|             if (ret)
 | |
|                 goto out;
 | |
|             cb(cbdata, princ);
 | |
|             free(princ);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   out:
 | |
|     krb5_clear_error_message(context->context);
 | |
| 
 | |
|   out_keep_error:
 | |
|     krb5_storage_free(sp);
 | |
|     krb5_data_free(&reply);
 | |
|     return ret;
 | |
| }
 |