# # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # include global definitions include ../../Makefile.master # # Build everything in parallel; use .WAIT for dependencies .PARALLEL: SUBDIRS= \ dyn \ .WAIT \ kdb \ .WAIT \ kadm5 \ .WAIT \ ss \ plugins all : TARGET= all install : TARGET= install clean : TARGET= clean clobber : TARGET= clobber _msg : TARGET= _msg .KEEP_STATE: all install clean clobber _msg: $(SUBDIRS) $(filter-out .WAIT,$(SUBDIRS)): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include $(SRC)/lib/Makefile.lib ROOTLIBDIR= $(ROOT)/usr/lib/krb5 # Hammerhead: 64-bit only - flatten Kerberos library path ROOTLIBDIR64= $(ROOT)/usr/lib/krb5 # # Copyright 2003 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include $(SRC)/lib/Makefile.targ pics/%.o: ../%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) pics/%.o: $(KADM5DIR)/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) pics/$(COMMONOBJS): $(COMMONSRCS) $(COMPILE.c) -o $@ $(COMMONSRCS) _msg: $(MSGDOMAIN) .WAIT $(POFILE) $(RM) $(MSGDOMAIN)/$(POFILE) $(CP) $(POFILE) $(MSGDOMAIN) $(POFILE): $(DERIVED_FILES) .WAIT $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ $(MSGDOMAIN): $(INS.dir) * Copyright 1995 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * Copyright 1988-1992,1994-2003 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * Copyright (C) 1998 by the FundsXpress, INC. * * All rights reserved. * * Export of this software from the United States of America may require * a specific license from the United States Government. It is the * responsibility of any person or organization contemplating export to * obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. FundsXpress makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * Copyright (c) 1990, 1991, 1993-1995 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. Copyright 1987-1989 by the Student Information Processing Board of the Massachusetts Institute of Technology Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. M.I.T. and the M.I.T. S.I.P.B. make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by the University of California, Berkeley and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. PORTIONS OF KERBEROS LIBRARIES AND UTILITIES # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include ../Makefile.lib # Hammerhead: amd64-only SUBDIRS= $(MACH64) all : TARGET= all clean : TARGET= clean clobber : TARGET= clobber install : TARGET= install .KEEP_STATE: all clean clobber install: $(SUBDIRS) _msg: $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. LIBRARY= libdyn.a VERS= .1 DYNOBJS= \ dyn_create.o \ dyn_put.o \ dyn_debug.o \ dyn_delete.o \ dyn_size.o \ dyn_append.o \ dyn_realloc.o \ dyn_paranoid.o \ dyn_insert.o \ dyn_initzero.o OBJECTS= $(DYNOBJS) # include library definitions include ../../Makefile.lib SRCS= $(DYNOBJS:%.o=../%.c) LIBS= $(DYNLIB) include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 #override liblink INS.liblink= -$(RM) $@; $(SYMLINK) $(LIBLINKS)$(VERS) $@ CPPFLAGS += -D_REENTRANT -DHAVE_LIBSOCKET=1 -DHAVE_LIBNSL=1 \ -DHAVE_UNISTD_H=1 -DHAVE_UMASK=1 -DHAVE_SRAND48=1 \ -DHAVESRAND=1 -DHAVESRANDOM=1 -DHAVE_RE_COMP=1 \ -DHAVE_RE_EXEC=1 -DHAVE_REGCOMP=1 -DHAVE_REGEXEC=1 \ -DHAVE_COMPILE=1 # Hammerhead: -I.. must be in CPPFLAGS (not CFLAGS) to survive Makefile.master.64 reset CFLAGS += $(CCVERBOSE) CPPFLAGS += -I.. LDLIBS += -lc CERRWARN += -Wno-unused-variable SMOFF += no_if_block .KEEP_STATE: all: $(LIBS) # include library targets include ../../Makefile.targ FRC: # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2025 Hammerhead Project # include ../Makefile.com include $(SRC)/lib/Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) /* * Copyright (c) 1997-2000 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _KRB5_DYN_DYN_H #define _KRB5_DYN_DYN_H #ifdef __cplusplus extern "C" { #endif /* * This file is part of libdyn.a, the C Dynamic Object library. It * contains the public header file. * * There are no restrictions on this code; however, if you make any * changes, I request that you document them so that I do not get * credit or blame for your modifications. * * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) * and MIT-Project Athena, 1989. */ /* * dyn.h -- header file to be included by programs linking against * libdyn.a. */ typedef char *DynPtr; typedef struct _DynObject { DynPtr array; int el_size, num_el, size, inc; int debug, paranoid, initzero; } DynObjectRec, *DynObject; /* Function macros */ #define DynHigh(obj) (DynSize(obj) - 1) #define DynLow(obj) (0) /* Return status codes */ #define DYN_OK -1000 #define DYN_NOMEM -1001 #define DYN_BADINDEX -1002 #define DYN_BADVALUE -1003 /* Function declarations */ #ifdef __STDC__ #define P(args) args #else #define P(args) () #endif /* __STDC__ */ DynObject DynCreate P((int el_size, int inc)), DynCopy P((DynObject obj)); int DynDestroy P((DynObject obj)), DynRelease P((DynObject obj)); int DynAdd P((DynObject obj, void *el)); int DynPut P((DynObject obj, void *el, int idx)); int DynInsert P((DynObject obj, int idx, void *els, int num)); int DynDelete P((DynObject obj, int idx)); DynPtr DynGet P((DynObject obj, int num)); DynPtr DynArray P((DynObject obj)); int DynDebug P((DynObject obj, int state)); int DynParanoid P((DynObject obj, int state)); int DynInitzero P((DynObject obj, int state)); int DynSize P((DynObject obj)); int DynCapacity P((DynObject obj)); #undef P /* * N.B. The original code had the following comment line in it, after the last * #endif: * DO NOT ADD ANYTHING AFTER THIS #endif * * This is in violatation of hdrchk standards, and so has been removed. * If this causes any subsequent build problems, the build issues need * to be resolved in a different fashion. */ #ifdef __cplusplus } #endif #endif /* !_KRB5_DYN_DYN_H */ /* * Copyright (c) 1997-2000 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _KRB5_DYN_DYNP_H #define _KRB5_DYN_DYNP_H #ifdef __cplusplus extern "C" { #endif /* * This file is part of libdyn.a, the C Dynamic Object library. It * contains the private header file. * * There are no restrictions on this code; however, if you make any * changes, I request that you document them so that I do not get * credit or blame for your modifications. * * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) * and MIT-Project Athena, 1989. */ /* * dynP.h -- private header file included by source files for libdyn.a. */ #include "dyn.h" #ifdef USE_DBMALLOC #include #include #endif /* * Rep invariant: * 1) el_size is the number of bytes per element in the object * 2) num_el is the number of elements currently in the object. It is * one higher than the highest index at which an element lives. * 3) size is the number of elements the object can hold without * resizing. num_el <= index. * 4) inc is a multiple of the number of elements the object grows by * each time it is reallocated. */ typedef struct _DynObject DynObjectRecP, *DynObjectP; /* Internal functions */ int _DynRealloc(), _DynResize(); /* * N.B. The original code had the following comment line after that last #endif: * DON'T ADD STUFF AFTER THIS #endif * * Ignoring the fact that this line itself was after the #endif, the line * caused unacceptable hdrchk breakage. If this results in a build breakage, * the build MUST be fixed in a more acceptable fashion. */ #ifdef __cplusplus } #endif #endif /* !_KRB5_DYN_DYNP_H */ /* * This file is part of libdyn.a, the C Dynamic Object library. It * contains the source code for the function DynAppend(). * * There are no restrictions on this code; however, if you make any * changes, I request that you document them so that I do not get * credit or blame for your modifications. * * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) * and MIT-Project Athena, 1989. */ #include #include "dynP.h" /* * Made obsolete by DynInsert, now just a convenience function. */ int DynAppend(obj, els, num) DynObjectP obj; DynPtr els; int num; { return DynInsert(obj, DynSize(obj), els, num); } /* * This file is part of libdyn.a, the C Dynamic Object library. It * contains the source code for the functions DynCreate() and * DynDestroy(). * * There are no restrictions on this code; however, if you make any * changes, I request that you document them so that I do not get * credit or blame for your modifications. * * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) * and MIT-Project Athena, 1989. */ #include #include #include #include "dynP.h" #ifndef DEFAULT_INC #define DEFAULT_INC 100 #endif static int default_increment = DEFAULT_INC; DynObjectP DynCreate(el_size, inc) int el_size, inc; { DynObjectP obj; obj = (DynObjectP) malloc(sizeof(DynObjectRecP)); if (obj == NULL) return NULL; #ifdef USE_DBMALLOC obj->array = (DynPtr) malloc(1); #else obj->array = (DynPtr) malloc(0); #endif obj->el_size = el_size; obj->num_el = obj->size = 0; obj->debug = obj->paranoid = 0; obj->inc = (!! inc) ? inc : default_increment; return obj; } DynObjectP DynCopy(obj) DynObjectP obj; { DynObjectP obj1; obj1 = (DynObjectP) malloc(sizeof(DynObjectRecP)); if (obj1 == NULL) return NULL; obj1->el_size = obj->el_size; obj1->num_el = obj->num_el; obj1->size = obj->size; obj1->inc = obj->inc; obj1->debug = obj->debug; obj1->paranoid = obj->paranoid; obj1->initzero = obj->initzero; obj1->array = (char *) malloc(obj1->el_size * obj1->size); if (obj1->array == NULL) { free(obj1); return NULL; } memcpy(obj->array, obj1->array, (size_t) (obj1->el_size * obj1->size)); return obj1; } int DynDestroy(obj) DynObjectP obj; { if (obj->paranoid) { if (obj->debug) fprintf(stderr, "dyn: destroy: zeroing %d bytes from %d.\n", obj->el_size * obj->size, obj->array); memset(obj->array, 0, obj->el_size * obj->size); } free(obj->array); free(obj); return DYN_OK; } int DynRelease(obj) DynObjectP obj; { if (obj->debug) fprintf(stderr, "dyn: release: freeing object structure.\n"); free(obj); return DYN_OK; } /* * This file is part of libdyn.a, the C Dynamic Object library. It * contains the source code for the function DynDebug(). * * There are no restrictions on this code; however, if you make any * changes, I request that you document them so that I do not get * credit or blame for your modifications. * * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) * and MIT-Project Athena, 1989. */ #include #include "dynP.h" int DynDebug(obj, state) DynObjectP obj; int state; { obj->debug = state; fprintf(stderr, "dyn: debug: Debug state set to %d.\n", state); return DYN_OK; } /* * This file is part of libdyn.a, the C Dynamic Object library. It * contains the source code for the function DynDelete(). * * There are no restrictions on this code; however, if you make any * changes, I request that you document them so that I do not get * credit or blame for your modifications. * * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) * and MIT-Project Athena, 1989. * Copyright (c) 2016 by Delphix. All rights reserved. */ #include #include #include #include "dynP.h" /* * Checkers! Get away from that "hard disk erase" button! * (Stupid dog. He almost did it to me again ...) */ int DynDelete(obj, idx) DynObjectP obj; int idx; { if (idx < 0) { if (obj->debug) fprintf(stderr, "dyn: delete: bad index %d\n", idx); return DYN_BADINDEX; } if (idx >= obj->num_el) { if (obj->debug) fprintf(stderr, "dyn: delete: Highest index is %d.\n", obj->num_el); return DYN_BADINDEX; } if (idx == obj->num_el-1) { if (obj->paranoid) { if (obj->debug) fprintf(stderr, "dyn: delete: last element, zeroing.\n"); memset(obj->array + idx*obj->el_size, 0, obj->el_size); } else { if (obj->debug) fprintf(stderr, "dyn: delete: last element, punting.\n"); } } else { if (obj->debug) fprintf(stderr, "dyn: delete: copying %d bytes from %d + %d to + %d.\n", obj->el_size*(obj->num_el - idx), obj->array, (idx+1)*obj->el_size, idx*obj->el_size); #ifdef HAVE_MEMMOVE memmove(obj->array + idx*obj->el_size, obj->array + (idx+1)*obj->el_size, obj->el_size*(obj->num_el - idx)); #else bcopy(obj->array + (idx+1)*obj->el_size, obj->array + idx*obj->el_size, obj->el_size*(obj->num_el - idx)); #endif if (obj->paranoid) { if (obj->debug) fprintf(stderr, "dyn: delete: zeroing %d bytes from %d + %d\n", obj->el_size, obj->array, obj->el_size*(obj->num_el - 1)); memset(obj->array + obj->el_size*(obj->num_el - 1), 0, obj->el_size); } } --obj->num_el; if (obj->debug) fprintf(stderr, "dyn: delete: done.\n"); return DYN_OK; } /* * This file is part of libdyn.a, the C Dynamic Object library. It * contains the source code for the function DynInitZero(). * * There are no restrictions on this code; however, if you make any * changes, I request that you document them so that I do not get * credit or blame for your modifications. * * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) * and MIT-Project Athena, 1989. */ #include #include "dynP.h" int DynInitzero(obj, state) DynObjectP obj; int state; { obj->initzero = state; if (obj->debug) fprintf(stderr, "dyn: initzero: initzero set to %d.\n", state); return DYN_OK; } /* * This file is part of libdyn.a, the C Dynamic Object library. It * contains the source code for the function DynInsert(). * * There are no restrictions on this code; however, if you make any * changes, I request that you document them so that I do not get * credit or blame for your modifications. * * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) * and MIT-Project Athena, 1989. */ #include #include #include "dynP.h" int DynInsert(obj, idx, els_in, num) DynObjectP obj; void *els_in; int idx, num; { DynPtr els = (DynPtr) els_in; int ret; if (idx < 0 || idx > obj->num_el) { if (obj->debug) fprintf(stderr, "dyn: insert: index %d is not in [0,%d]\n", idx, obj->num_el); return DYN_BADINDEX; } if (num < 1) { if (obj->debug) fprintf(stderr, "dyn: insert: cannot insert %d elements\n", num); return DYN_BADVALUE; } if (obj->debug) fprintf(stderr,"dyn: insert: Moving %d bytes from %d + %d to + %d\n", (obj->num_el-idx)*obj->el_size, obj->array, obj->el_size*idx, obj->el_size*(idx+num)); if ((ret = _DynResize(obj, obj->num_el + num)) != DYN_OK) return ret; #ifdef HAVE_MEMMOVE memmove(obj->array + obj->el_size*(idx + num), obj->array + obj->el_size*idx, (obj->num_el-idx)*obj->el_size); #else bcopy(obj->array + obj->el_size*idx, obj->array + obj->el_size*(idx + num), (obj->num_el-idx)*obj->el_size); #endif if (obj->debug) fprintf(stderr, "dyn: insert: Copying %d bytes from %d to %d + %d\n", obj->el_size*num, els, obj->array, obj->el_size*idx); #ifdef HAVE_MEMMOVE memmove(obj->array + obj->el_size*idx, els, obj->el_size*num); #else bcopy(els, obj->array + obj->el_size*idx, obj->el_size*num); #endif obj->num_el += num; if (obj->debug) fprintf(stderr, "dyn: insert: done.\n"); return DYN_OK; } /* * This file is part of libdyn.a, the C Dynamic Object library. It * contains the source code for the function DynDebug(). * * There are no restrictions on this code; however, if you make any * changes, I request that you document them so that I do not get * credit or blame for your modifications. * * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) * and MIT-Project Athena, 1989. */ #include #include "dynP.h" int DynParanoid(obj, state) DynObjectP obj; int state; { obj->paranoid = state; if (obj->debug) fprintf(stderr, "dyn: paranoid: Paranoia set to %d.\n", state); return DYN_OK; } /* * This file is part of libdyn.a, the C Dynamic Object library. It * contains the source code for the functions DynGet() and DynAdd(). * * There are no restrictions on this code; however, if you make any * changes, I request that you document them so that I do not get * credit or blame for your modifications. * * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) * and MIT-Project Athena, 1989. */ #include #include #include "dynP.h" DynPtr DynArray(obj) DynObjectP obj; { if (obj->debug) fprintf(stderr, "dyn: array: returning array pointer %d.\n", obj->array); return obj->array; } DynPtr DynGet(obj, num) DynObjectP obj; int num; { if (num < 0) { if (obj->debug) fprintf(stderr, "dyn: get: bad index %d\n", num); return NULL; } if (num >= obj->num_el) { if (obj->debug) fprintf(stderr, "dyn: get: highest element is %d.\n", obj->num_el); return NULL; } if (obj->debug) fprintf(stderr, "dyn: get: Returning address %d + %d.\n", obj->array, obj->el_size*num); return (DynPtr) obj->array + obj->el_size*num; } int DynAdd(obj, el) DynObjectP obj; void *el; { int ret; ret = DynPut(obj, el, obj->num_el); if (ret != DYN_OK) return ret; ++obj->num_el; return ret; } /* * WARNING! There is a reason this function is not documented in the * man page. If DynPut used to mutate already existing elements, * everything will go fine. If it is used to add new elements * directly, however, the state within the object (such as * obj->num_el) will not be updated properly and many other functions * in the library will lose. Have a nice day. */ int DynPut(obj, el_in, idx) DynObjectP obj; void *el_in; int idx; { DynPtr el = (DynPtr) el_in; int ret; if (obj->debug) fprintf(stderr, "dyn: put: Writing %d bytes from %d to %d + %d\n", obj->el_size, el, obj->array, idx*obj->el_size); if ((ret = _DynResize(obj, idx)) != DYN_OK) return ret; #ifdef HAVE_MEMMOVE memmove(obj->array + idx*obj->el_size, el, obj->el_size); #else bcopy(el, obj->array + idx*obj->el_size, obj->el_size); #endif if (obj->debug) fprintf(stderr, "dyn: put: done.\n"); return DYN_OK; } /* * This file is part of libdyn.a, the C Dynamic Object library. It * contains the source code for the internal function _DynRealloc(). * * There are no restrictions on this code; however, if you make any * changes, I request that you document them so that I do not get * credit or blame for your modifications. * * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) * and MIT-Project Athena, 1989. */ #include #include #include "dynP.h" /* * Resize the array so that element req exists. */ int _DynResize(obj, req) DynObjectP obj; int req; { int cnt, size; if (obj->size > req) return DYN_OK; else if (obj->inc > 0) return _DynRealloc(obj, (req - obj->size) / obj->inc + 1); else { if (obj->size == 0) size = -obj->inc; else size = obj->size; while (size <= req) size <<= 1; return _DynRealloc(obj, size); } } /* * Resize the array by num_incs units. If obj->inc is positive, this * means make it obj->inc*num_incs elements larger. If obj->inc is * negative, this means make the array num_incs elements long. * * Ideally, this function should not be called from outside the * library. However, nothing will break if it is. */ int _DynRealloc(obj, num_incs) DynObjectP obj; int num_incs; { DynPtr temp; int new_size_in_bytes; if (obj->inc > 0) new_size_in_bytes = obj->el_size*(obj->size + obj->inc*num_incs); else new_size_in_bytes = obj->el_size*num_incs; if (obj->debug) fprintf(stderr, "dyn: alloc: Increasing object by %d bytes (%d incs).\n", new_size_in_bytes - obj->el_size*obj->size, num_incs); temp = (DynPtr) realloc(obj->array, new_size_in_bytes); if (temp == NULL) { if (obj->debug) fprintf(stderr, "dyn: alloc: Out of memory.\n"); return DYN_NOMEM; } else { obj->array = temp; if (obj->inc > 0) obj->size += obj->inc*num_incs; else obj->size = num_incs; } if (obj->debug) fprintf(stderr, "dyn: alloc: done.\n"); return DYN_OK; } /* * This file is part of libdyn.a, the C Dynamic Object library. It * contains the source code for the function DynSize(). * * There are no restrictions on this code; however, if you make any * changes, I request that you document them so that I do not get * credit or blame for your modifications. * * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) * and MIT-Project Athena, 1989. */ #include #include "dynP.h" int DynSize(obj) DynObjectP obj; { if (obj->debug) fprintf(stderr, "dyn: size: returning size %d.\n", obj->num_el); return obj->num_el; } int DynCapacity(obj) DynObjectP obj; { if (obj->debug) fprintf(stderr, "dyn: capacity: returning cap of %d.\n", obj->size); return obj->size; } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # # Due to mistakes made early in the history of this library, there are # no SUNW_1.1 symbols, but the version is now kept as a placeholder. # Don't add any symbols to this version. $mapfile_version 2 SYMBOL_VERSION SUNW_1.1 { global: SUNW_1.1; }; SYMBOL_VERSION SUNWprivate_1.1 { global: DynAdd; DynAppend; DynArray; DynCapacity; DynCopy; DynCreate; DynDebug; DynDelete; DynDestroy; DynGet; DynInitzero; DynInsert; DynParanoid; DynPut; DynRelease; DynSize; local: *; }; # # Copyright 2004 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # include global definitions include ../../../Makefile.master # # Build everything in parallel; use .WAIT for dependencies .PARALLEL: $(SUBDIRS) SUBDIRS= clnt srv all : TARGET= all clean : TARGET= clean clobber : TARGET= clobber install : TARGET= install _msg : TARGET= _msg .KEEP_STATE: all clean clobber install _msg: $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Data Types for policy and principal information that * exists in the respective databases. * * $Header$ * * This file was originally created with rpcgen. * It has been hacked up since then. */ #ifndef __ADB_H__ #define __ADB_H__ #include #include /* SUNWresync121 - no need to change to gssrpc/ */ #include "k5-int.h" #include #include #include #include #include typedef long osa_adb_ret_t; #define OSA_ADB_POLICY_DB_MAGIC 0x12345A00 #define OSA_ADB_PRINC_DB_MAGIC 0x12345B00 #define OSA_ADB_SHARED 0x7001 #define OSA_ADB_EXCLUSIVE 0x7002 #define OSA_ADB_PERMANENT 0x7003 #define OSA_ADB_PRINC_VERSION_MASK 0x12345C00 #define OSA_ADB_PRINC_VERSION_1 0x12345C01 #define OSA_ADB_POLICY_VERSION_MASK 0x12345D00 #define OSA_ADB_POLICY_VERSION_1 0x12345D01 typedef struct _osa_adb_db_lock_ent_t { FILE *lockfile; char *filename; int refcnt, lockmode, lockcnt; krb5_context context; } osa_adb_lock_ent, *osa_adb_lock_t; typedef struct _osa_adb_db_ent_t { int magic; DB *db; HASHINFO info; BTREEINFO btinfo; char *filename; osa_adb_lock_t lock; int opencnt; } osa_adb_db_ent, *osa_adb_db_t, *osa_adb_princ_t, *osa_adb_policy_t; /* an osa_pw_hist_ent stores all the key_datas for a single password */ typedef struct _osa_pw_hist_t { int n_key_data; krb5_key_data *key_data; } osa_pw_hist_ent, *osa_pw_hist_t; typedef struct _osa_princ_ent_t { int version; char *policy; long aux_attributes; unsigned int old_key_len; unsigned int old_key_next; krb5_kvno admin_history_kvno; osa_pw_hist_ent *old_keys; } osa_princ_ent_rec, *osa_princ_ent_t; typedef struct _osa_policy_ent_t { int version; char *name; uint32_t pw_min_life; uint32_t pw_max_life; uint32_t pw_min_length; uint32_t pw_min_classes; uint32_t pw_history_num; uint32_t policy_refcnt; } osa_policy_ent_rec, *osa_policy_ent_t; typedef void (*osa_adb_iter_princ_func) (void *, osa_princ_ent_t); typedef void (*osa_adb_iter_policy_func) (void *, osa_policy_ent_t); /* * Return Code (the rest are in adb_err.h) */ #define OSA_ADB_OK 0 /* * xdr functions */ bool_t xdr_osa_princ_ent_rec(XDR *xdrs, osa_princ_ent_t objp); bool_t xdr_osa_policy_ent_rec(XDR *xdrs, osa_policy_ent_t objp); bool_t xdr_osa_pw_hist_ent(XDR *xdrs, osa_pw_hist_ent *objp); bool_t xdr_krb5_key_data(XDR *xdrs, krb5_key_data *objp); /* * Functions */ osa_adb_ret_t osa_adb_create_db(char *filename, char *lockfile, int magic); osa_adb_ret_t osa_adb_destroy_db(char *filename, char *lockfile, int magic); osa_adb_ret_t osa_adb_rename_db(char *filefrom, char *lockfrom, char *fileto, char *lockto, int magic); osa_adb_ret_t osa_adb_rename_policy_db(kadm5_config_params *fromparams, kadm5_config_params *toparams); osa_adb_ret_t osa_adb_init_db(osa_adb_db_t *dbp, char *filename, char *lockfile, int magic); osa_adb_ret_t osa_adb_fini_db(osa_adb_db_t db, int magic); osa_adb_ret_t osa_adb_get_lock(osa_adb_db_t db, int mode); osa_adb_ret_t osa_adb_release_lock(osa_adb_db_t db); osa_adb_ret_t osa_adb_open_and_lock(osa_adb_princ_t db, int locktype); osa_adb_ret_t osa_adb_close_and_unlock(osa_adb_princ_t db); osa_adb_ret_t osa_adb_create_policy_db(kadm5_config_params *params); osa_adb_ret_t osa_adb_destroy_policy_db(kadm5_config_params *params); osa_adb_ret_t osa_adb_open_princ(osa_adb_princ_t *db, char *filename); osa_adb_ret_t osa_adb_open_policy(osa_adb_policy_t *db, kadm5_config_params *rparams); osa_adb_ret_t osa_adb_close_princ(osa_adb_princ_t db); osa_adb_ret_t osa_adb_close_policy(osa_adb_policy_t db); osa_adb_ret_t osa_adb_create_princ(osa_adb_princ_t db, osa_princ_ent_t entry); osa_adb_ret_t osa_adb_create_policy(osa_adb_policy_t db, osa_policy_ent_t entry); osa_adb_ret_t osa_adb_destroy_princ(osa_adb_princ_t db, kadm5_princ_t name); osa_adb_ret_t osa_adb_destroy_policy(osa_adb_policy_t db, kadm5_policy_t name); osa_adb_ret_t osa_adb_get_princ(osa_adb_princ_t db, kadm5_princ_t name, osa_princ_ent_t *entry); osa_adb_ret_t osa_adb_get_policy(osa_adb_policy_t db, kadm5_policy_t name, osa_policy_ent_t *entry); osa_adb_ret_t osa_adb_put_princ(osa_adb_princ_t db, osa_princ_ent_t entry); osa_adb_ret_t osa_adb_put_policy(osa_adb_policy_t db, osa_policy_ent_t entry); osa_adb_ret_t osa_adb_iter_policy(osa_adb_policy_t db, osa_adb_iter_policy_func func, void * data); osa_adb_ret_t osa_adb_iter_princ(osa_adb_princ_t db, osa_adb_iter_princ_func func, void *data); void osa_free_policy_ent(osa_policy_ent_t val); void osa_free_princ_ent(osa_princ_ent_t val); #endif /* __ADB_H__ */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef __KADM5_ADMIN_H__ #define __KADM5_ADMIN_H__ #ifdef __cplusplus extern "C" { #endif /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * lib/kadm5/admin.h * * Copyright 2001 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ */ #include #include #include #include #include #include #include #include #include #define KADM5_ADMIN_SERVICE_P "kadmin@admin" /* * Solaris Kerberos: * The kadmin/admin principal is unused on Solaris. This principal is used * in AUTH_GSSAPI but Solaris doesn't support AUTH_GSSAPI. RPCSEC_GSS can only * be used with host-based principals. * */ /* #define KADM5_ADMIN_SERVICE "kadmin/admin" */ #define KADM5_CHANGEPW_SERVICE_P "kadmin@changepw" #define KADM5_CHANGEPW_SERVICE "kadmin/changepw" #define KADM5_HIST_PRINCIPAL "kadmin/history" #define KADM5_ADMIN_HOST_SERVICE "kadmin" #define KADM5_CHANGEPW_HOST_SERVICE "changepw" #define KADM5_KIPROP_HOST_SERVICE "kiprop" typedef krb5_principal kadm5_princ_t; typedef char *kadm5_policy_t; typedef long kadm5_ret_t; typedef int rpc_int32; typedef unsigned int rpc_u_int32; #define KADM5_PW_FIRST_PROMPT \ (error_message(CHPASS_UTIL_NEW_PASSWORD_PROMPT)) #define KADM5_PW_SECOND_PROMPT \ (error_message(CHPASS_UTIL_NEW_PASSWORD_AGAIN_PROMPT)) /* * Successful return code */ #define KADM5_OK 0 /* * Field masks */ /* kadm5_principal_ent_t */ #define KADM5_PRINCIPAL 0x000001 #define KADM5_PRINC_EXPIRE_TIME 0x000002 #define KADM5_PW_EXPIRATION 0x000004 #define KADM5_LAST_PWD_CHANGE 0x000008 #define KADM5_ATTRIBUTES 0x000010 #define KADM5_MAX_LIFE 0x000020 #define KADM5_MOD_TIME 0x000040 #define KADM5_MOD_NAME 0x000080 #define KADM5_KVNO 0x000100 #define KADM5_MKVNO 0x000200 #define KADM5_AUX_ATTRIBUTES 0x000400 #define KADM5_POLICY 0x000800 #define KADM5_POLICY_CLR 0x001000 /* version 2 masks */ #define KADM5_MAX_RLIFE 0x002000 #define KADM5_LAST_SUCCESS 0x004000 #define KADM5_LAST_FAILED 0x008000 #define KADM5_FAIL_AUTH_COUNT 0x010000 #define KADM5_KEY_DATA 0x020000 #define KADM5_TL_DATA 0x040000 #ifdef notyet /* Novell */ #define KADM5_CPW_FUNCTION 0x080000 #define KADM5_RANDKEY_USED 0x100000 #endif #define KADM5_LOAD 0x200000 /* Solaris Kerberos: adding support for key history in LDAP KDB */ #define KADM5_KEY_HIST 0x400000 /* all but KEY_DATA and TL_DATA */ #define KADM5_PRINCIPAL_NORMAL_MASK 0x01ffff /* kadm5_policy_ent_t */ #define KADM5_PW_MAX_LIFE 0x004000 #define KADM5_PW_MIN_LIFE 0x008000 #define KADM5_PW_MIN_LENGTH 0x010000 #define KADM5_PW_MIN_CLASSES 0x020000 #define KADM5_PW_HISTORY_NUM 0x040000 #define KADM5_REF_COUNT 0x080000 /* kadm5_config_params */ #define KADM5_CONFIG_REALM 0x0000001 #define KADM5_CONFIG_DBNAME 0x0000002 #define KADM5_CONFIG_MKEY_NAME 0x0000004 #define KADM5_CONFIG_MAX_LIFE 0x0000008 #define KADM5_CONFIG_MAX_RLIFE 0x0000010 #define KADM5_CONFIG_EXPIRATION 0x0000020 #define KADM5_CONFIG_FLAGS 0x0000040 #define KADM5_CONFIG_ADMIN_KEYTAB 0x0000080 #define KADM5_CONFIG_STASH_FILE 0x0000100 #define KADM5_CONFIG_ENCTYPE 0x0000200 #define KADM5_CONFIG_ADBNAME 0x0000400 #define KADM5_CONFIG_ADB_LOCKFILE 0x0000800 #define KADM5_CONFIG_PROFILE 0x0001000 #define KADM5_CONFIG_ACL_FILE 0x0002000 #define KADM5_CONFIG_KADMIND_PORT 0x0004000 #define KADM5_CONFIG_ENCTYPES 0x0008000 #define KADM5_CONFIG_ADMIN_SERVER 0x0010000 #define KADM5_CONFIG_DICT_FILE 0x0020000 #define KADM5_CONFIG_MKEY_FROM_KBD 0x0040000 #define KADM5_CONFIG_KPASSWD_PORT 0x0080000 #define KADM5_CONFIG_KPASSWD_SERVER 0x0100000 #define KADM5_CONFIG_KPASSWD_PROTOCOL 0x0200000 #define KADM5_CONFIG_IPROP_ENABLED 0x0400000 #define KADM5_CONFIG_ULOG_SIZE 0x0800000 #define KADM5_CONFIG_POLL_TIME 0x1000000 /* password change constants */ #define KRB5_KPASSWD_SUCCESS 0 #define KRB5_KPASSWD_MALFORMED 1 #define KRB5_KPASSWD_HARDERROR 2 #define KRB5_KPASSWD_AUTHERROR 3 #define KRB5_KPASSWD_SOFTERROR 4 #define KRB5_KPASSWD_ACCESSDENIED 5 #define KRB5_KPASSWD_BAD_VERSION 6 #define KRB5_KPASSWD_INITIAL_FLAG_NEEDED 7 #define KRB5_KPASSWD_POLICY_REJECT 8 #define KRB5_KPASSWD_BAD_PRINCIPAL 9 #define KRB5_KPASSWD_ETYPE_NOSUPP 10 /* * permission bits */ #define KADM5_PRIV_GET 0x01 #define KADM5_PRIV_ADD 0x02 #define KADM5_PRIV_MODIFY 0x04 #define KADM5_PRIV_DELETE 0x08 /* * API versioning constants */ #define KADM5_MASK_BITS 0xffffff00 #define KADM5_STRUCT_VERSION_MASK 0x12345600 #define KADM5_STRUCT_VERSION_1 (KADM5_STRUCT_VERSION_MASK|0x01) #define KADM5_STRUCT_VERSION KADM5_STRUCT_VERSION_1 #define KADM5_API_VERSION_MASK 0x12345700 #define KADM5_API_VERSION_1 (KADM5_API_VERSION_MASK|0x01) #define KADM5_API_VERSION_2 (KADM5_API_VERSION_MASK|0x02) #ifdef KRB5_DNS_LOOKUP /* * Name length constants for DNS lookups */ #define MAX_HOST_NAMELEN 256 #define MAX_DNS_NAMELEN (15*(MAX_HOST_NAMELEN + 1)+1) #endif /* KRB5_DNS_LOOKUP */ typedef struct _kadm5_principal_ent_t_v2 { krb5_principal principal; krb5_timestamp princ_expire_time; krb5_timestamp last_pwd_change; krb5_timestamp pw_expiration; krb5_deltat max_life; krb5_principal mod_name; krb5_timestamp mod_date; krb5_flags attributes; krb5_kvno kvno; krb5_kvno mkvno; char *policy; long aux_attributes; /* version 2 fields */ krb5_deltat max_renewable_life; krb5_timestamp last_success; krb5_timestamp last_failed; krb5_kvno fail_auth_count; krb5_int16 n_key_data; krb5_int16 n_tl_data; krb5_tl_data *tl_data; krb5_key_data *key_data; } kadm5_principal_ent_rec_v2, *kadm5_principal_ent_t_v2; typedef struct _kadm5_principal_ent_t_v1 { krb5_principal principal; krb5_timestamp princ_expire_time; krb5_timestamp last_pwd_change; krb5_timestamp pw_expiration; krb5_deltat max_life; krb5_principal mod_name; krb5_timestamp mod_date; krb5_flags attributes; krb5_kvno kvno; krb5_kvno mkvno; char *policy; long aux_attributes; } kadm5_principal_ent_rec_v1, *kadm5_principal_ent_t_v1; #if USE_KADM5_API_VERSION == 1 typedef struct _kadm5_principal_ent_t_v1 kadm5_principal_ent_rec, *kadm5_principal_ent_t; #else typedef struct _kadm5_principal_ent_t_v2 kadm5_principal_ent_rec, *kadm5_principal_ent_t; #endif typedef struct _kadm5_policy_ent_t { char *policy; long pw_min_life; long pw_max_life; long pw_min_length; long pw_min_classes; long pw_history_num; long policy_refcnt; } kadm5_policy_ent_rec, *kadm5_policy_ent_t; /* * New types to indicate which protocol to use when sending * password change requests */ typedef enum { KRB5_CHGPWD_RPCSEC, KRB5_CHGPWD_CHANGEPW_V2 } krb5_chgpwd_prot; /* * Data structure returned by kadm5_get_config_params() */ typedef struct _kadm5_config_params { long mask; char * realm; int kadmind_port; int kpasswd_port; char * admin_server; #ifdef notyet /* Novell */ /* ABI change? */ char * kpasswd_server; #endif char * dbname; char * admin_dbname; char * admin_lockfile; char * admin_keytab; char * acl_file; char * dict_file; int mkey_from_kbd; char * stash_file; char * mkey_name; krb5_enctype enctype; krb5_deltat max_life; krb5_deltat max_rlife; krb5_timestamp expiration; krb5_flags flags; krb5_key_salt_tuple *keysalts; krb5_int32 num_keysalts; char *kpasswd_server; krb5_chgpwd_prot kpasswd_protocol; bool_t iprop_enabled; int iprop_ulogsize; char *iprop_polltime; } kadm5_config_params; /*********************************************************************** * This is the old krb5_realm_read_params, which I mutated into * kadm5_get_config_params but which old code (kdb5_* and krb5kdc) * still uses. ***********************************************************************/ /* * Data structure returned by krb5_read_realm_params() */ typedef struct __krb5_realm_params { char * realm_profile; char * realm_dbname; char * realm_mkey_name; char * realm_stash_file; char * realm_kdc_ports; char * realm_kdc_tcp_ports; char * realm_acl_file; krb5_int32 realm_kadmind_port; krb5_enctype realm_enctype; krb5_deltat realm_max_life; krb5_deltat realm_max_rlife; krb5_timestamp realm_expiration; krb5_flags realm_flags; krb5_key_salt_tuple *realm_keysalts; unsigned int realm_reject_bad_transit:1; unsigned int realm_kadmind_port_valid:1; unsigned int realm_enctype_valid:1; unsigned int realm_max_life_valid:1; unsigned int realm_max_rlife_valid:1; unsigned int realm_expiration_valid:1; unsigned int realm_flags_valid:1; unsigned int realm_reject_bad_transit_valid:1; krb5_int32 realm_num_keysalts; } krb5_realm_params; /* * functions */ kadm5_ret_t kadm5_get_adm_host_srv_name(krb5_context context, const char *realm, char **host_service_name); kadm5_ret_t kadm5_get_cpw_host_srv_name(krb5_context context, const char *realm, char **host_service_name); #if USE_KADM5_API_VERSION > 1 krb5_error_code kadm5_get_config_params(krb5_context context, int use_kdc_config, kadm5_config_params *params_in, kadm5_config_params *params_out); krb5_error_code kadm5_free_config_params(krb5_context context, kadm5_config_params *params); krb5_error_code kadm5_free_realm_params(krb5_context kcontext, kadm5_config_params *params); krb5_error_code kadm5_get_admin_service_name(krb5_context, char *, char *, size_t); #endif kadm5_ret_t kadm5_init(char *client_name, char *pass, char *service_name, #if USE_KADM5_API_VERSION == 1 char *realm, #else kadm5_config_params *params, #endif krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle); kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass, char *service_name, #if USE_KADM5_API_VERSION == 1 char *realm, #else kadm5_config_params *params, #endif krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle); kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab, char *service_name, #if USE_KADM5_API_VERSION == 1 char *realm, #else kadm5_config_params *params, #endif krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle); #if USE_KADM5_API_VERSION > 1 kadm5_ret_t kadm5_init_with_creds(char *client_name, krb5_ccache cc, char *service_name, kadm5_config_params *params, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle); #endif kadm5_ret_t kadm5_lock(void *server_handle); kadm5_ret_t kadm5_unlock(void *server_handle); kadm5_ret_t kadm5_flush(void *server_handle); kadm5_ret_t kadm5_destroy(void *server_handle); kadm5_ret_t kadm5_check_min_life(void *server_handle, /* Solaris Kerberos */ krb5_principal principal, char *msg_ret, unsigned int msg_len); kadm5_ret_t kadm5_create_principal(void *server_handle, kadm5_principal_ent_t ent, long mask, char *pass); kadm5_ret_t kadm5_create_principal_3(void *server_handle, kadm5_principal_ent_t ent, long mask, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char *pass); kadm5_ret_t kadm5_delete_principal(void *server_handle, krb5_principal principal); kadm5_ret_t kadm5_modify_principal(void *server_handle, kadm5_principal_ent_t ent, long mask); kadm5_ret_t kadm5_rename_principal(void *server_handle, krb5_principal,krb5_principal); #if USE_KADM5_API_VERSION == 1 kadm5_ret_t kadm5_get_principal(void *server_handle, krb5_principal principal, kadm5_principal_ent_t *ent); #else kadm5_ret_t kadm5_get_principal(void *server_handle, krb5_principal principal, kadm5_principal_ent_t ent, long mask); #endif kadm5_ret_t kadm5_chpass_principal(void *server_handle, krb5_principal principal, char *pass); kadm5_ret_t kadm5_chpass_principal_3(void *server_handle, krb5_principal principal, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char *pass); #if USE_KADM5_API_VERSION == 1 kadm5_ret_t kadm5_randkey_principal(void *server_handle, krb5_principal principal, krb5_keyblock **keyblock); #else /* * Solaris Kerberos: * this routine is only implemented in the client library. */ kadm5_ret_t kadm5_randkey_principal_old(void *server_handle, krb5_principal principal, krb5_keyblock **keyblocks, int *n_keys); kadm5_ret_t kadm5_randkey_principal(void *server_handle, krb5_principal principal, krb5_keyblock **keyblocks, int *n_keys); kadm5_ret_t kadm5_randkey_principal_3(void *server_handle, krb5_principal principal, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, krb5_keyblock **keyblocks, int *n_keys); #endif kadm5_ret_t kadm5_setv4key_principal(void *server_handle, krb5_principal principal, krb5_keyblock *keyblock); kadm5_ret_t kadm5_setkey_principal(void *server_handle, krb5_principal principal, krb5_keyblock *keyblocks, int n_keys); kadm5_ret_t kadm5_setkey_principal_3(void *server_handle, krb5_principal principal, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, krb5_keyblock *keyblocks, int n_keys); kadm5_ret_t kadm5_decrypt_key(void *server_handle, kadm5_principal_ent_t entry, krb5_int32 ktype, krb5_int32 stype, krb5_int32 kvno, krb5_keyblock *keyblock, krb5_keysalt *keysalt, int *kvnop); kadm5_ret_t kadm5_create_policy(void *server_handle, kadm5_policy_ent_t ent, long mask); /* * kadm5_create_policy_internal is not part of the supported, * exposed API. It is available only in the server library, and you * shouldn't use it unless you know why it's there and how it's * different from kadm5_create_policy. */ kadm5_ret_t kadm5_create_policy_internal(void *server_handle, kadm5_policy_ent_t entry, long mask); kadm5_ret_t kadm5_delete_policy(void *server_handle, kadm5_policy_t policy); kadm5_ret_t kadm5_modify_policy(void *server_handle, kadm5_policy_ent_t ent, long mask); /* * kadm5_modify_policy_internal is not part of the supported, * exposed API. It is available only in the server library, and you * shouldn't use it unless you know why it's there and how it's * different from kadm5_modify_policy. */ kadm5_ret_t kadm5_modify_policy_internal(void *server_handle, kadm5_policy_ent_t entry, long mask); #if USE_KADM5_API_VERSION == 1 kadm5_ret_t kadm5_get_policy(void *server_handle, kadm5_policy_t policy, kadm5_policy_ent_t *ent); #else kadm5_ret_t kadm5_get_policy(void *server_handle, kadm5_policy_t policy, kadm5_policy_ent_t ent); #endif kadm5_ret_t kadm5_get_privs(void *server_handle, long *privs); kadm5_ret_t kadm5_chpass_principal_util(void *server_handle, krb5_principal princ, char *new_pw, char **ret_pw, char *msg_ret, unsigned int msg_len); kadm5_ret_t kadm5_free_principal_ent(void *server_handle, kadm5_principal_ent_t ent); kadm5_ret_t kadm5_free_policy_ent(void *server_handle, kadm5_policy_ent_t ent); kadm5_ret_t kadm5_get_principals(void *server_handle, char *exp, char ***princs, int *count); kadm5_ret_t kadm5_get_policies(void *server_handle, char *exp, char ***pols, int *count); #if USE_KADM5_API_VERSION > 1 kadm5_ret_t kadm5_free_key_data(void *server_handle, krb5_int16 *n_key_data, krb5_key_data *key_data); #endif kadm5_ret_t kadm5_free_name_list(void *server_handle, char **names, int count); krb5_error_code kadm5_init_krb5_context (krb5_context *); #if USE_KADM5_API_VERSION == 1 /* * OVSEC_KADM_API_VERSION_1 should be, if possible, compile-time * compatible with KADM5_API_VERSION_2. Basically, this means we have * to continue to provide all the old ovsec_kadm function and symbol * names. */ #define OVSEC_KADM_ACLFILE "/krb5/ovsec_adm.acl" #define OVSEC_KADM_WORDFILE "/krb5/ovsec_adm.dict" #define OVSEC_KADM_ADMIN_SERVICE "ovsec_adm/admin" #define OVSEC_KADM_CHANGEPW_SERVICE "ovsec_adm/changepw" #define OVSEC_KADM_HIST_PRINCIPAL "ovsec_adm/history" typedef krb5_principal ovsec_kadm_princ_t; typedef krb5_keyblock ovsec_kadm_keyblock; typedef char *ovsec_kadm_policy_t; typedef long ovsec_kadm_ret_t; enum ovsec_kadm_salttype { OVSEC_KADM_SALT_V4, OVSEC_KADM_SALT_NORMAL }; enum ovsec_kadm_saltmod { OVSEC_KADM_MOD_KEEP, OVSEC_KADM_MOD_V4, OVSEC_KADM_MOD_NORMAL }; #define OVSEC_KADM_PW_FIRST_PROMPT \ ((char *) error_message(CHPASS_UTIL_NEW_PASSWORD_PROMPT)) #define OVSEC_KADM_PW_SECOND_PROMPT \ ((char *) error_message(CHPASS_UTIL_NEW_PASSWORD_AGAIN_PROMPT)) /* * Successful return code */ #define OVSEC_KADM_OK 0 /* * Create/Modify masks */ /* principal */ #define OVSEC_KADM_PRINCIPAL 0x000001 #define OVSEC_KADM_PRINC_EXPIRE_TIME 0x000002 #define OVSEC_KADM_PW_EXPIRATION 0x000004 #define OVSEC_KADM_LAST_PWD_CHANGE 0x000008 #define OVSEC_KADM_ATTRIBUTES 0x000010 #define OVSEC_KADM_MAX_LIFE 0x000020 #define OVSEC_KADM_MOD_TIME 0x000040 #define OVSEC_KADM_MOD_NAME 0x000080 #define OVSEC_KADM_KVNO 0x000100 #define OVSEC_KADM_MKVNO 0x000200 #define OVSEC_KADM_AUX_ATTRIBUTES 0x000400 #define OVSEC_KADM_POLICY 0x000800 #define OVSEC_KADM_POLICY_CLR 0x001000 /* policy */ #define OVSEC_KADM_PW_MAX_LIFE 0x004000 #define OVSEC_KADM_PW_MIN_LIFE 0x008000 #define OVSEC_KADM_PW_MIN_LENGTH 0x010000 #define OVSEC_KADM_PW_MIN_CLASSES 0x020000 #define OVSEC_KADM_PW_HISTORY_NUM 0x040000 #define OVSEC_KADM_REF_COUNT 0x080000 /* * permission bits */ #define OVSEC_KADM_PRIV_GET 0x01 #define OVSEC_KADM_PRIV_ADD 0x02 #define OVSEC_KADM_PRIV_MODIFY 0x04 #define OVSEC_KADM_PRIV_DELETE 0x08 /* * API versioning constants */ #define OVSEC_KADM_MASK_BITS 0xffffff00 #define OVSEC_KADM_STRUCT_VERSION_MASK 0x12345600 #define OVSEC_KADM_STRUCT_VERSION_1 (OVSEC_KADM_STRUCT_VERSION_MASK|0x01) #define OVSEC_KADM_STRUCT_VERSION OVSEC_KADM_STRUCT_VERSION_1 #define OVSEC_KADM_API_VERSION_MASK 0x12345700 #define OVSEC_KADM_API_VERSION_1 (OVSEC_KADM_API_VERSION_MASK|0x01) typedef struct _ovsec_kadm_principal_ent_t { krb5_principal principal; krb5_timestamp princ_expire_time; krb5_timestamp last_pwd_change; krb5_timestamp pw_expiration; krb5_deltat max_life; krb5_principal mod_name; krb5_timestamp mod_date; krb5_flags attributes; krb5_kvno kvno; krb5_kvno mkvno; char *policy; long aux_attributes; } ovsec_kadm_principal_ent_rec, *ovsec_kadm_principal_ent_t; typedef struct _ovsec_kadm_policy_ent_t { char *policy; long pw_min_life; long pw_max_life; long pw_min_length; long pw_min_classes; long pw_history_num; long policy_refcnt; } ovsec_kadm_policy_ent_rec, *ovsec_kadm_policy_ent_t; /* * functions */ ovsec_kadm_ret_t ovsec_kadm_init(char *client_name, char *pass, char *service_name, char *realm, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle); ovsec_kadm_ret_t ovsec_kadm_init_with_password(char *client_name, char *pass, char *service_name, char *realm, krb5_ui_4 struct_version, krb5_ui_4 api_version, char ** db_args, void **server_handle); ovsec_kadm_ret_t ovsec_kadm_init_with_skey(char *client_name, char *keytab, char *service_name, char *realm, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle); ovsec_kadm_ret_t ovsec_kadm_flush(void *server_handle); ovsec_kadm_ret_t ovsec_kadm_destroy(void *server_handle); ovsec_kadm_ret_t ovsec_kadm_create_principal(void *server_handle, ovsec_kadm_principal_ent_t ent, long mask, char *pass); ovsec_kadm_ret_t ovsec_kadm_delete_principal(void *server_handle, krb5_principal principal); ovsec_kadm_ret_t ovsec_kadm_modify_principal(void *server_handle, ovsec_kadm_principal_ent_t ent, long mask); ovsec_kadm_ret_t ovsec_kadm_rename_principal(void *server_handle, krb5_principal,krb5_principal); ovsec_kadm_ret_t ovsec_kadm_get_principal(void *server_handle, krb5_principal principal, ovsec_kadm_principal_ent_t *ent); ovsec_kadm_ret_t ovsec_kadm_chpass_principal(void *server_handle, krb5_principal principal, char *pass); ovsec_kadm_ret_t ovsec_kadm_randkey_principal(void *server_handle, krb5_principal principal, krb5_keyblock **keyblock); ovsec_kadm_ret_t ovsec_kadm_create_policy(void *server_handle, ovsec_kadm_policy_ent_t ent, long mask); /* * ovsec_kadm_create_policy_internal is not part of the supported, * exposed API. It is available only in the server library, and you * shouldn't use it unless you know why it's there and how it's * different from ovsec_kadm_create_policy. */ ovsec_kadm_ret_t ovsec_kadm_create_policy_internal(void *server_handle, ovsec_kadm_policy_ent_t entry, long mask); ovsec_kadm_ret_t ovsec_kadm_delete_policy(void *server_handle, ovsec_kadm_policy_t policy); ovsec_kadm_ret_t ovsec_kadm_modify_policy(void *server_handle, ovsec_kadm_policy_ent_t ent, long mask); /* * ovsec_kadm_modify_policy_internal is not part of the supported, * exposed API. It is available only in the server library, and you * shouldn't use it unless you know why it's there and how it's * different from ovsec_kadm_modify_policy. */ ovsec_kadm_ret_t ovsec_kadm_modify_policy_internal(void *server_handle, ovsec_kadm_policy_ent_t entry, long mask); ovsec_kadm_ret_t ovsec_kadm_get_policy(void *server_handle, ovsec_kadm_policy_t policy, ovsec_kadm_policy_ent_t *ent); ovsec_kadm_ret_t ovsec_kadm_get_privs(void *server_handle, long *privs); ovsec_kadm_ret_t ovsec_kadm_chpass_principal_util(void *server_handle, krb5_principal princ, char *new_pw, char **ret_pw, char *msg_ret); ovsec_kadm_ret_t ovsec_kadm_free_principal_ent(void *server_handle, ovsec_kadm_principal_ent_t ent); ovsec_kadm_ret_t ovsec_kadm_free_policy_ent(void *server_handle, ovsec_kadm_policy_ent_t ent); ovsec_kadm_ret_t ovsec_kadm_free_name_list(void *server_handle, char **names, int count); ovsec_kadm_ret_t ovsec_kadm_get_principals(void *server_handle, char *exp, char ***princs, int *count); ovsec_kadm_ret_t ovsec_kadm_get_policies(void *server_handle, char *exp, char ***pols, int *count); #define OVSEC_KADM_FAILURE KADM5_FAILURE #define OVSEC_KADM_AUTH_GET KADM5_AUTH_GET #define OVSEC_KADM_AUTH_ADD KADM5_AUTH_ADD #define OVSEC_KADM_AUTH_MODIFY KADM5_AUTH_MODIFY #define OVSEC_KADM_AUTH_DELETE KADM5_AUTH_DELETE #define OVSEC_KADM_AUTH_INSUFFICIENT KADM5_AUTH_INSUFFICIENT #define OVSEC_KADM_BAD_DB KADM5_BAD_DB #define OVSEC_KADM_DUP KADM5_DUP #define OVSEC_KADM_RPC_ERROR KADM5_RPC_ERROR #define OVSEC_KADM_NO_SRV KADM5_NO_SRV #define OVSEC_KADM_BAD_HIST_KEY KADM5_BAD_HIST_KEY #define OVSEC_KADM_NOT_INIT KADM5_NOT_INIT #define OVSEC_KADM_UNK_PRINC KADM5_UNK_PRINC #define OVSEC_KADM_UNK_POLICY KADM5_UNK_POLICY #define OVSEC_KADM_BAD_MASK KADM5_BAD_MASK #define OVSEC_KADM_BAD_CLASS KADM5_BAD_CLASS #define OVSEC_KADM_BAD_LENGTH KADM5_BAD_LENGTH #define OVSEC_KADM_BAD_POLICY KADM5_BAD_POLICY #define OVSEC_KADM_BAD_PRINCIPAL KADM5_BAD_PRINCIPAL #define OVSEC_KADM_BAD_AUX_ATTR KADM5_BAD_AUX_ATTR #define OVSEC_KADM_BAD_HISTORY KADM5_BAD_HISTORY #define OVSEC_KADM_BAD_MIN_PASS_LIFE KADM5_BAD_MIN_PASS_LIFE #define OVSEC_KADM_PASS_Q_TOOSHORT KADM5_PASS_Q_TOOSHORT #define OVSEC_KADM_PASS_Q_CLASS KADM5_PASS_Q_CLASS #define OVSEC_KADM_PASS_Q_DICT KADM5_PASS_Q_DICT #define OVSEC_KADM_PASS_REUSE KADM5_PASS_REUSE #define OVSEC_KADM_PASS_TOOSOON KADM5_PASS_TOOSOON #define OVSEC_KADM_POLICY_REF KADM5_POLICY_REF #define OVSEC_KADM_INIT KADM5_INIT #define OVSEC_KADM_BAD_PASSWORD KADM5_BAD_PASSWORD #define OVSEC_KADM_PROTECT_PRINCIPAL KADM5_PROTECT_PRINCIPAL #define OVSEC_KADM_BAD_SERVER_HANDLE KADM5_BAD_SERVER_HANDLE #define OVSEC_KADM_BAD_STRUCT_VERSION KADM5_BAD_STRUCT_VERSION #define OVSEC_KADM_OLD_STRUCT_VERSION KADM5_OLD_STRUCT_VERSION #define OVSEC_KADM_NEW_STRUCT_VERSION KADM5_NEW_STRUCT_VERSION #define OVSEC_KADM_BAD_API_VERSION KADM5_BAD_API_VERSION #define OVSEC_KADM_OLD_LIB_API_VERSION KADM5_OLD_LIB_API_VERSION #define OVSEC_KADM_OLD_SERVER_API_VERSION KADM5_OLD_SERVER_API_VERSION #define OVSEC_KADM_NEW_LIB_API_VERSION KADM5_NEW_LIB_API_VERSION #define OVSEC_KADM_NEW_SERVER_API_VERSION KADM5_NEW_SERVER_API_VERSION #define OVSEC_KADM_SECURE_PRINC_MISSING KADM5_SECURE_PRINC_MISSING #define OVSEC_KADM_NO_RENAME_SALT KADM5_NO_RENAME_SALT #endif /* USE_KADM5_API_VERSION == 1 */ #define MAXPRINCLEN 125 void trunc_name(size_t *len, char **dots); krb5_chgpwd_prot _kadm5_get_kpasswd_protocol(void *server_handle); kadm5_ret_t kadm5_chpass_principal_v2(void *server_handle, krb5_principal princ, char *new_password, kadm5_ret_t *srvr_rsp_code, krb5_data *srvr_msg); void handle_chpw(krb5_context context, int s, void *serverhandle, kadm5_config_params *params); #ifdef __cplusplus } #endif #endif /* __KADM5_ADMIN_H__ */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * */ #ifndef __KADM5_ADMIN_INTERNAL_H__ #define __KADM5_ADMIN_INTERNAL_H__ #include #ifdef DEBUG #define ADMIN_LOG(a, b, c) syslog(a, b, c); #define ADMIN_LOGO(a, b) syslog(a, b); #else #define ADMIN_LOG(a, b, c) #define ADMIN_LOGO(a, b) #endif #define KADM5_SERVER_HANDLE_MAGIC 0x12345800 #define GENERIC_CHECK_HANDLE(handle, old_api_version, new_api_version) \ { \ kadm5_server_handle_t srvr = \ (kadm5_server_handle_t) handle; \ \ if (! srvr) \ return KADM5_BAD_SERVER_HANDLE; \ if (srvr->magic_number != KADM5_SERVER_HANDLE_MAGIC) \ return KADM5_BAD_SERVER_HANDLE; \ if ((srvr->struct_version & KADM5_MASK_BITS) != \ KADM5_STRUCT_VERSION_MASK) \ return KADM5_BAD_STRUCT_VERSION; \ if (srvr->struct_version < KADM5_STRUCT_VERSION_1) \ return KADM5_OLD_STRUCT_VERSION; \ if (srvr->struct_version > KADM5_STRUCT_VERSION_1) \ return KADM5_NEW_STRUCT_VERSION; \ if ((srvr->api_version & KADM5_MASK_BITS) != \ KADM5_API_VERSION_MASK) \ return KADM5_BAD_API_VERSION; \ if (srvr->api_version < KADM5_API_VERSION_1) \ return old_api_version; \ if (srvr->api_version > KADM5_API_VERSION_2) \ return new_api_version; \ } /* * _KADM5_CHECK_HANDLE calls the function _kadm5_check_handle and * returns any non-zero error code that function returns. * _kadm5_check_handle, in client_handle.c and server_handle.c, exists * in both the server- and client- side libraries. In each library, * it calls CHECK_HANDLE, which is defined by the appropriate * _internal.h header file to call GENERIC_CHECK_HANDLE as well as * CLIENT_CHECK_HANDLE and SERVER_CHECK_HANDLE. * * _KADM5_CHECK_HANDLE should be used by a function that needs to * check the handle but wants to be the same code in both the client * and server library; it makes a function call to the right handle * checker. Code that only exists in one library can call the * CHECK_HANDLE macro, which inlines the test instead of making * another function call. * * Got that? */ #define _KADM5_CHECK_HANDLE(handle) \ { int ecode; if ((ecode = _kadm5_check_handle((void *)handle))) return ecode;} int _kadm5_check_handle(void *handle); kadm5_ret_t _kadm5_chpass_principal_util(void *server_handle, void *lhandle, krb5_principal princ, char *new_pw, char **ret_pw, char *msg_ret, unsigned int msg_len); /* this is needed by the alt_prof code I stole. The functions maybe shouldn't be named krb5_*, but they are. */ krb5_error_code krb5_string_to_keysalts(char *string, const char *tupleseps, const char *ksaltseps, krb5_boolean dups, krb5_key_salt_tuple **ksaltp, krb5_int32 *nksaltp); krb5_error_code krb5_string_to_flags(char* string, const char* positive, const char* negative, krb5_flags *flagsp); #endif /* __KADM5_ADMIN_INTERNAL_H__ */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ * */ #include #include "kadm_rpc.h" #include "server_internal.h" bool_t xdr_ui_4(XDR *xdrs, krb5_ui_4 *objp); bool_t xdr_nullstring(XDR *xdrs, char **objp); bool_t xdr_nulltype(XDR *xdrs, void **objp, xdrproc_t proc); bool_t xdr_krb5_timestamp(XDR *xdrs, krb5_timestamp *objp); bool_t xdr_krb5_kvno(XDR *xdrs, krb5_kvno *objp); bool_t xdr_krb5_deltat(XDR *xdrs, krb5_deltat *objp); bool_t xdr_krb5_flags(XDR *xdrs, krb5_flags *objp); bool_t xdr_krb5_ui_4(XDR *xdrs, krb5_ui_4 *objp); bool_t xdr_krb5_int16(XDR *xdrs, krb5_int16 *objp); bool_t xdr_krb5_ui_2(XDR *xdrs, krb5_ui_2 *objp); bool_t xdr_krb5_key_data_nocontents(XDR *xdrs, krb5_key_data *objp); bool_t xdr_krb5_key_salt_tuple(XDR *xdrs, krb5_key_salt_tuple *objp); bool_t xdr_krb5_tl_data(XDR *xdrs, krb5_tl_data **tl_data_head); bool_t xdr_kadm5_ret_t(XDR *xdrs, kadm5_ret_t *objp); bool_t xdr_kadm5_principal_ent_rec_v1(XDR *xdrs, kadm5_principal_ent_rec *objp); bool_t xdr_kadm5_principal_ent_rec(XDR *xdrs, kadm5_principal_ent_rec *objp); bool_t xdr_kadm5_policy_ent_rec(XDR *xdrs, kadm5_policy_ent_rec *objp); bool_t xdr_kadm5_policy_ent_t(XDR *xdrs, kadm5_policy_ent_t *objp); bool_t xdr_kadm5_principal_ent_t(XDR *xdrs, kadm5_principal_ent_t *objp); bool_t xdr_cprinc_arg(XDR *xdrs, cprinc_arg *objp); bool_t xdr_cprinc3_arg(XDR *xdrs, cprinc3_arg *objp); bool_t xdr_generic_ret(XDR *xdrs, generic_ret *objp); bool_t xdr_dprinc_arg(XDR *xdrs, dprinc_arg *objp); bool_t xdr_mprinc_arg(XDR *xdrs, mprinc_arg *objp); bool_t xdr_rprinc_arg(XDR *xdrs, rprinc_arg *objp); bool_t xdr_chpass_arg(XDR *xdrs, chpass_arg *objp); bool_t xdr_chpass3_arg(XDR *xdrs, chpass3_arg *objp); bool_t xdr_setv4key_arg(XDR *xdrs, setv4key_arg *objp); bool_t xdr_setkey_arg(XDR *xdrs, setkey_arg *objp); bool_t xdr_setkey3_arg(XDR *xdrs, setkey3_arg *objp); bool_t xdr_chrand_arg(XDR *xdrs, chrand_arg *objp); bool_t xdr_chrand3_arg(XDR *xdrs, chrand3_arg *objp); bool_t xdr_chrand_ret(XDR *xdrs, chrand_ret *objp); bool_t xdr_gprinc_arg(XDR *xdrs, gprinc_arg *objp); bool_t xdr_gprinc_ret(XDR *xdrs, gprinc_ret *objp); bool_t xdr_gprincs_arg(XDR *xdrs, gprincs_arg *objp); bool_t xdr_gprincs_ret(XDR *xdrs, gprincs_ret *objp); bool_t xdr_cpol_arg(XDR *xdrs, cpol_arg *objp); bool_t xdr_dpol_arg(XDR *xdrs, dpol_arg *objp); bool_t xdr_mpol_arg(XDR *xdrs, mpol_arg *objp); bool_t xdr_gpol_arg(XDR *xdrs, gpol_arg *objp); bool_t xdr_gpol_ret(XDR *xdrs, gpol_ret *objp); bool_t xdr_gpols_arg(XDR *xdrs, gpols_arg *objp); bool_t xdr_gpols_ret(XDR *xdrs, gpols_ret *objp); bool_t xdr_getprivs_ret(XDR *xdrs, getprivs_ret *objp); bool_t xdr_krb5_principal(XDR *xdrs, krb5_principal *objp); bool_t xdr_krb5_octet(XDR *xdrs, krb5_octet *objp); bool_t xdr_krb5_int32(XDR *xdrs, krb5_int32 *objp); bool_t xdr_krb5_enctype(XDR *xdrs, krb5_enctype *objp); bool_t xdr_krb5_salttype(XDR *xdrs, krb5_int32 *objp); bool_t xdr_krb5_keyblock(XDR *xdrs, krb5_keyblock *objp); bool_t xdr_krb5_key_data(XDR *xdrs, krb5_key_data *objp); bool_t xdr_osa_pw_hist_ent(XDR *xdrs, osa_pw_hist_ent *objp); /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * lib/kadm/alt_prof.c * * Copyright 1995,2001 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ /* * alt_prof.c - Implement alternate profile file handling. */ #include "k5-int.h" #include #include "adm_proto.h" #include #include #include #include krb5_error_code kadm5_free_config_params(); #define DEFAULT_ENCTYPE_LIST \ "aes256-cts-hmac-sha1-96:normal " \ "aes128-cts-hmac-sha1-96:normal " \ "des3-cbc-hmac-sha1-kd:normal " \ "arcfour-hmac-md5:normal " \ "arcfour-hmac-md5-exp:normal " \ "des-cbc-md5:normal " \ "des-cbc-crc:normal" static krb5_key_salt_tuple *copy_key_salt_tuple(ksalt, len) krb5_key_salt_tuple *ksalt; krb5_int32 len; { krb5_key_salt_tuple *knew; if((knew = (krb5_key_salt_tuple *) malloc((len ) * sizeof(krb5_key_salt_tuple)))) { memcpy(knew, ksalt, len * sizeof(krb5_key_salt_tuple)); return knew; } return 0; } /* * krb5_aprof_init() - Initialize alternate profile context. * * Parameters: * fname - default file name of the profile. * envname - environment variable name which can override fname. * acontextp - Pointer to opaque context for alternate profile. * * Returns: * error codes from profile_init() */ krb5_error_code krb5_aprof_init(fname, envname, acontextp) char *fname; char *envname; krb5_pointer *acontextp; { krb5_error_code kret; profile_t profile; const char *kdc_config; size_t krb5_config_len, kdc_config_len; char *profile_path; char **filenames; int i; kret = krb5_get_default_config_files (&filenames); if (kret) return kret; krb5_config_len = 0; for (i = 0; filenames[i] != NULL; i++) krb5_config_len += strlen(filenames[i]) + 1; if (i > 0) krb5_config_len--; if (envname == NULL || (kdc_config = getenv(envname)) == NULL) kdc_config = fname; if (kdc_config == NULL) kdc_config_len = 0; else kdc_config_len = strlen(kdc_config); profile_path = malloc(2 + krb5_config_len + kdc_config_len); if (profile_path == NULL) { krb5_free_config_files(filenames); return errno; } if (kdc_config_len) strcpy(profile_path, kdc_config); else profile_path[0] = 0; if (krb5_config_len) for (i = 0; filenames[i] != NULL; i++) { if (kdc_config_len || i) strcat(profile_path, ":"); strcat(profile_path, filenames[i]); } krb5_free_config_files(filenames); profile = (profile_t) NULL; kret = profile_init_path(profile_path, &profile); free(profile_path); if (kret) return kret; *acontextp = profile; return 0; } /* * krb5_aprof_getvals() - Get values from alternate profile. * * Parameters: * acontext - opaque context for alternate profile. * hierarchy - hierarchy of value to retrieve. * retdata - Returned data values. * * Returns: * error codes from profile_get_values() */ krb5_error_code krb5_aprof_getvals(acontext, hierarchy, retdata) krb5_pointer acontext; const char **hierarchy; char ***retdata; { return(profile_get_values((profile_t) acontext, hierarchy, retdata)); } /* * krb5_aprof_get_boolean() * * Parameters: * acontext - opaque context for alternate profile * hierarchy - hierarchy of value to retrieve * retdata - Returned data value * Returns: * error codes */ static krb5_error_code string_to_boolean (const char *string, krb5_boolean *out) { static const char *const yes[] = { "y", "yes", "true", "t", "1", "on" }; static const char *const no[] = { "n", "no", "false", "f", "nil", "0", "off" }; int i; for (i = 0; i < sizeof(yes)/sizeof(yes[0]); i++) if (!strcasecmp(string, yes[i])) { *out = 1; return 0; } for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) if (!strcasecmp(string, no[i])) { *out = 0; return 0; } return PROF_BAD_BOOLEAN; } krb5_error_code krb5_aprof_get_boolean(krb5_pointer acontext, const char **hierarchy, int uselast, krb5_boolean *retdata) { krb5_error_code kret; char **values; char *valp; int idx; krb5_boolean val; kret = krb5_aprof_getvals (acontext, hierarchy, &values); if (kret) return kret; idx = 0; if (uselast) { while (values[idx]) idx++; idx--; } valp = values[idx]; kret = string_to_boolean (valp, &val); if (kret) return kret; *retdata = val; return 0; } /* * krb5_aprof_get_deltat() - Get a delta time value from the alternate * profile. * * Parameters: * acontext - opaque context for alternate profile. * hierarchy - hierarchy of value to retrieve. * uselast - if true, use last value, otherwise use * first value found. * deltatp - returned delta time value. * * Returns: * error codes from profile_get_values() * error codes from krb5_string_to_deltat() */ krb5_error_code krb5_aprof_get_deltat(acontext, hierarchy, uselast, deltatp) krb5_pointer acontext; const char **hierarchy; krb5_boolean uselast; krb5_deltat *deltatp; { krb5_error_code kret; char **values; char *valp; int idx; if (!(kret = krb5_aprof_getvals(acontext, hierarchy, &values))) { idx = 0; if (uselast) { for (idx=0; values[idx]; idx++); idx--; } valp = values[idx]; kret = krb5_string_to_deltat(valp, deltatp); /* Free the string storage */ for (idx=0; values[idx]; idx++) krb5_xfree(values[idx]); krb5_xfree(values); } return(kret); } /* * krb5_aprof_get_string() - Get a string value from the alternate * profile. * * Parameters: * acontext - opaque context for alternate profile. * hierarchy - hierarchy of value to retrieve. * uselast - if true, use last value, otherwise use * first value found. * stringp - returned string value. * * Returns: * error codes from profile_get_values() */ krb5_error_code krb5_aprof_get_string(acontext, hierarchy, uselast, stringp) krb5_pointer acontext; const char **hierarchy; krb5_boolean uselast; char **stringp; { krb5_error_code kret; char **values; int idx, i; if (!(kret = krb5_aprof_getvals(acontext, hierarchy, &values))) { idx = 0; if (uselast) { for (idx=0; values[idx]; idx++); idx--; } *stringp = values[idx]; /* Free the string storage */ for (i=0; values[i]; i++) if (i != idx) krb5_xfree(values[i]); krb5_xfree(values); } return(kret); } /* * krb5_aprof_get_int32() - Get a 32-bit integer value from the alternate * profile. * * Parameters: * acontext - opaque context for alternate profile. * hierarchy - hierarchy of value to retrieve. * uselast - if true, use last value, otherwise use * first value found. * intp - returned 32-bit integer value. * * Returns: * error codes from profile_get_values() * EINVAL - value is not an integer */ krb5_error_code krb5_aprof_get_int32(acontext, hierarchy, uselast, intp) krb5_pointer acontext; const char **hierarchy; krb5_boolean uselast; krb5_int32 *intp; { krb5_error_code kret; char **values; int idx; if (!(kret = krb5_aprof_getvals(acontext, hierarchy, &values))) { idx = 0; if (uselast) { for (idx=0; values[idx]; idx++); idx--; } if (sscanf(values[idx], "%d", intp) != 1) kret = EINVAL; /* Free the string storage */ for (idx=0; values[idx]; idx++) krb5_xfree(values[idx]); krb5_xfree(values); } return(kret); } /* * krb5_aprof_finish() - Finish alternate profile context. * * Parameter: * acontext - opaque context for alternate profile. * * Returns: * 0 on success, something else on failure. */ krb5_error_code krb5_aprof_finish(acontext) krb5_pointer acontext; { profile_release(acontext); return(0); } /* * Function: kadm5_get_config_params * * Purpose: Merge configuration parameters provided by the caller with * values specified in configuration files and with default values. * * Arguments: * * context (r) krb5_context to use * profile (r) profile file to use * envname (r) envname that contains a profile name to * override profile * params_in (r) params structure containing user-supplied * values, or NULL * params_out (w) params structure to be filled in * * Effects: * * The fields and mask of params_out are filled in with values * obtained from params_in, the specified profile, and default * values. Only and all fields specified in params_out->mask are * set. The context of params_out must be freed with * kadm5_free_config_params. * * params_in and params_out may be the same pointer. However, all pointers * in params_in for which the mask is set will be re-assigned to newly copied * versions, overwriting the old pointer value. */ krb5_error_code kadm5_get_config_params(context, use_kdc_config, params_in, params_out) krb5_context context; int use_kdc_config; kadm5_config_params *params_in, *params_out; { char *filename; char *envname; char *lrealm; krb5_pointer aprofile = 0; const char *hierarchy[4]; char *svalue; krb5_int32 ivalue; kadm5_config_params params, empty_params; krb5_error_code kret = 0; krb5_error_code dnsret = 1; #ifdef KRB5_DNS_LOOKUP char dns_host[MAX_DNS_NAMELEN]; unsigned short dns_portno; krb5_data dns_realm; memset((char *)&dns_realm, 0, sizeof (dns_realm)); #endif /* KRB5_DNS_LOOKUP */ memset((char *) ¶ms, 0, sizeof(params)); memset((char *) &empty_params, 0, sizeof(empty_params)); if (params_in == NULL) params_in = &empty_params; if (params_in->mask & KADM5_CONFIG_REALM) { lrealm = params.realm = strdup(params_in->realm); if (params.realm) params.mask |= KADM5_CONFIG_REALM; } else { kret = krb5_get_default_realm(context, &lrealm); if (kret) goto cleanup; params.realm = lrealm; params.mask |= KADM5_CONFIG_REALM; } /* * XXX These defaults should to work on both client and * server. kadm5_get_config_params can be implemented as a * wrapper function in each library that provides correct * defaults for NULL values. */ if (use_kdc_config) { filename = DEFAULT_KDC_PROFILE; envname = KDC_PROFILE_ENV; } else { filename = DEFAULT_PROFILE_PATH; envname = "KRB5_CONFIG"; } if (context->profile_secure == TRUE) envname = 0; kret = krb5_aprof_init(filename, envname, &aprofile); if (kret) goto cleanup; /* Initialize realm parameters */ hierarchy[0] = "realms"; hierarchy[1] = lrealm; hierarchy[3] = (char *) NULL; #ifdef KRB5_DNS_LOOKUP /* * Initialize realm info for (possible) DNS lookups. */ dns_realm.data = strdup(lrealm); dns_realm.length = strlen(lrealm); dns_realm.magic = 0; #endif /* KRB5_DNS_LOOKUP */ /* Get the value for the admin server */ hierarchy[2] = "admin_server"; if (params_in->mask & KADM5_CONFIG_ADMIN_SERVER) { params.admin_server = strdup(params_in->admin_server); if (params.admin_server) params.mask |= KADM5_CONFIG_ADMIN_SERVER; } else if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { params.admin_server = svalue; params.mask |= KADM5_CONFIG_ADMIN_SERVER; } #ifdef KRB5_DNS_LOOKUP else if (strcmp(envname, "KRB5_CONFIG") == 0) { /* * Solaris Kerberos: only do DNS lookup for admin_server if this * is a krb5.conf type of config file. Note, the filename may * not be /etc/krb5/krb5.conf so we assume that the KRB5_CONFIG * envname string will consistently indicate the type of config * file. */ dnsret = krb5_get_servername(context, &dns_realm, "_kerberos-adm", "_udp", dns_host, &dns_portno); if (dnsret == 0) { params.admin_server = strdup(dns_host); if (params.admin_server) params.mask |= KADM5_CONFIG_ADMIN_SERVER; params.kadmind_port = dns_portno; params.mask |= KADM5_CONFIG_KADMIND_PORT; } } #endif /* KRB5_DNS_LOOKUP */ if ((params.mask & KADM5_CONFIG_ADMIN_SERVER) && dnsret) { char *p; p = strchr(params.admin_server, ':'); if (p) { params.kadmind_port = atoi(p+1); params.mask |= KADM5_CONFIG_KADMIND_PORT; *p = '\0'; } } /* Get the value for the database */ hierarchy[2] = "database_name"; if (params_in->mask & KADM5_CONFIG_DBNAME) { params.dbname = strdup(params_in->dbname); if (params.dbname) params.mask |= KADM5_CONFIG_DBNAME; } else if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { params.dbname = svalue; params.mask |= KADM5_CONFIG_DBNAME; } else { params.dbname = strdup(DEFAULT_KDB_FILE); if (params.dbname) params.mask |= KADM5_CONFIG_DBNAME; } /* * admin database name and lockfile are now always derived from dbname */ if (params.mask & KADM5_CONFIG_DBNAME) { params.admin_dbname = (char *) malloc(strlen(params.dbname) + 7); if (params.admin_dbname) { sprintf(params.admin_dbname, "%s.kadm5", params.dbname); params.mask |= KADM5_CONFIG_ADBNAME; } } if (params.mask & KADM5_CONFIG_ADBNAME) { params.admin_lockfile = (char *) malloc(strlen(params.admin_dbname) + 6); if (params.admin_lockfile) { sprintf(params.admin_lockfile, "%s.lock", params.admin_dbname); params.mask |= KADM5_CONFIG_ADB_LOCKFILE; } } /* Get the value for the admin (policy) database lock file*/ hierarchy[2] = "admin_keytab"; if (params_in->mask & KADM5_CONFIG_ADMIN_KEYTAB) { params.admin_keytab = strdup(params_in->admin_keytab); if (params.admin_keytab) params.mask |= KADM5_CONFIG_ADMIN_KEYTAB; } else if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { params.mask |= KADM5_CONFIG_ADMIN_KEYTAB; params.admin_keytab = svalue; } else if ((params.admin_keytab = (char *) getenv("KRB5_KTNAME"))) { params.admin_keytab = strdup(params.admin_keytab); if (params.admin_keytab) params.mask |= KADM5_CONFIG_ADMIN_KEYTAB; } else { params.admin_keytab = strdup(DEFAULT_KADM5_KEYTAB); if (params.admin_keytab) params.mask |= KADM5_CONFIG_ADMIN_KEYTAB; } /* Get the name of the acl file */ hierarchy[2] = "acl_file"; if (params_in->mask & KADM5_CONFIG_ACL_FILE) { params.acl_file = strdup(params_in->acl_file); if (params.acl_file) params.mask |= KADM5_CONFIG_ACL_FILE; } else if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { params.mask |= KADM5_CONFIG_ACL_FILE; params.acl_file = svalue; } else { params.acl_file = strdup(DEFAULT_KADM5_ACL_FILE); if (params.acl_file) params.mask |= KADM5_CONFIG_ACL_FILE; } /* Get the name of the dict file */ hierarchy[2] = "dict_file"; if (params_in->mask & KADM5_CONFIG_DICT_FILE) { params.dict_file = strdup(params_in->dict_file); if (params.dict_file) params.mask |= KADM5_CONFIG_DICT_FILE; } else if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { params.mask |= KADM5_CONFIG_DICT_FILE; params.dict_file = svalue; } /* Get the value for the kadmind port */ if (! (params.mask & KADM5_CONFIG_KADMIND_PORT)) { hierarchy[2] = "kadmind_port"; if (params_in->mask & KADM5_CONFIG_KADMIND_PORT) { params.mask |= KADM5_CONFIG_KADMIND_PORT; params.kadmind_port = params_in->kadmind_port; } else if (aprofile && !krb5_aprof_get_int32(aprofile, hierarchy, TRUE, &ivalue)) { params.kadmind_port = ivalue; params.mask |= KADM5_CONFIG_KADMIND_PORT; } else { params.kadmind_port = DEFAULT_KADM5_PORT; params.mask |= KADM5_CONFIG_KADMIND_PORT; } } /* Get the value for the kpasswd port */ if (! (params.mask & KADM5_CONFIG_KPASSWD_PORT)) { hierarchy[2] = "kpasswd_port"; if (params_in->mask & KADM5_CONFIG_KPASSWD_PORT) { params.mask |= KADM5_CONFIG_KPASSWD_PORT; params.kpasswd_port = params_in->kpasswd_port; } else if (aprofile && !krb5_aprof_get_int32(aprofile, hierarchy, TRUE, &ivalue)) { params.kpasswd_port = ivalue; params.mask |= KADM5_CONFIG_KPASSWD_PORT; } else { params.kpasswd_port = DEFAULT_KPASSWD_PORT; params.mask |= KADM5_CONFIG_KPASSWD_PORT; } } /* Get the value for the master key name */ hierarchy[2] = "master_key_name"; if (params_in->mask & KADM5_CONFIG_MKEY_NAME) { params.mkey_name = strdup(params_in->mkey_name); if (params.mkey_name) params.mask |= KADM5_CONFIG_MKEY_NAME; } else if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { params.mask |= KADM5_CONFIG_MKEY_NAME; params.mkey_name = svalue; } /* Get the value for the master key type */ hierarchy[2] = "master_key_type"; if (params_in->mask & KADM5_CONFIG_ENCTYPE) { params.mask |= KADM5_CONFIG_ENCTYPE; params.enctype = params_in->enctype; } else if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { if (!krb5_string_to_enctype(svalue, ¶ms.enctype)) { params.mask |= KADM5_CONFIG_ENCTYPE; krb5_xfree(svalue); } } else { params.mask |= KADM5_CONFIG_ENCTYPE; params.enctype = DEFAULT_KDC_ENCTYPE; } /* Get the value for mkey_from_kbd */ if (params_in->mask & KADM5_CONFIG_MKEY_FROM_KBD) { params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; params.mkey_from_kbd = params_in->mkey_from_kbd; } /* Get the value for the stashfile */ hierarchy[2] = "key_stash_file"; if (params_in->mask & KADM5_CONFIG_STASH_FILE) { params.stash_file = strdup(params_in->stash_file); if (params.stash_file) params.mask |= KADM5_CONFIG_STASH_FILE; } else if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { params.mask |= KADM5_CONFIG_STASH_FILE; params.stash_file = svalue; } /* * Solaris Kerberos * Get the value for maximum ticket lifetime. * See SEAM documentation or the Bug ID 4184504 * We have changed the logic so that the entries are * created in the database with the maximum duration * for life and renew life KRB5_INT32_MAX * However this wil get negotiated down when * as or tgs request is processed by KDC. */ hierarchy[2] = "max_life"; if (params_in->mask & KADM5_CONFIG_MAX_LIFE) { params.mask |= KADM5_CONFIG_MAX_LIFE; params.max_life = params_in->max_life; } else { params.max_life = KRB5_INT32_MAX; params.mask |= KADM5_CONFIG_MAX_LIFE; } /* Get the value for maximum renewable ticket lifetime. */ hierarchy[2] = "max_renewable_life"; if (params_in->mask & KADM5_CONFIG_MAX_RLIFE) { params.mask |= KADM5_CONFIG_MAX_RLIFE; params.max_rlife = params_in->max_rlife; } else { params.max_rlife = KRB5_INT32_MAX; params.mask |= KADM5_CONFIG_MAX_RLIFE; } /* Get the value for the default principal expiration */ hierarchy[2] = "default_principal_expiration"; if (params_in->mask & KADM5_CONFIG_EXPIRATION) { params.mask |= KADM5_CONFIG_EXPIRATION; params.expiration = params_in->expiration; } else if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { if (!krb5_string_to_timestamp(svalue, ¶ms.expiration)) { params.mask |= KADM5_CONFIG_EXPIRATION; krb5_xfree(svalue); } } else { params.mask |= KADM5_CONFIG_EXPIRATION; params.expiration = 0; } /* Get the value for the default principal flags */ hierarchy[2] = "default_principal_flags"; if (params_in->mask & KADM5_CONFIG_FLAGS) { params.mask |= KADM5_CONFIG_FLAGS; params.flags = params_in->flags; } else if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { char *sp, *ep, *tp; sp = svalue; params.flags = 0; while (sp) { if ((ep = strchr(sp, (int) ',')) || (ep = strchr(sp, (int) ' ')) || (ep = strchr(sp, (int) '\t'))) { /* Fill in trailing whitespace of sp */ tp = ep - 1; while (isspace((int) *tp) && (tp > sp)) { *tp = '\0'; tp--; } *ep = '\0'; ep++; /* Skip over trailing whitespace of ep */ while (isspace((int) *ep) && (*ep)) ep++; } /* Convert this flag */ if (krb5_string_to_flags(sp, "+", "-", ¶ms.flags)) break; sp = ep; } if (!sp) params.mask |= KADM5_CONFIG_FLAGS; krb5_xfree(svalue); } else { params.mask |= KADM5_CONFIG_FLAGS; params.flags = KRB5_KDB_DEF_FLAGS; } /* Get the value for the supported enctype/salttype matrix */ hierarchy[2] = "supported_enctypes"; if (params_in->mask & KADM5_CONFIG_ENCTYPES) { params.mask |= KADM5_CONFIG_ENCTYPES; if (params_in->num_keysalts > 0) { params.keysalts = malloc(params_in->num_keysalts * sizeof (*params.keysalts)); if (params.keysalts == NULL) { kret = ENOMEM; goto cleanup; } (void) memcpy(params.keysalts, params_in->keysalts, (params_in->num_keysalts * sizeof (*params.keysalts))); params.num_keysalts = params_in->num_keysalts; } } else { svalue = NULL; if (aprofile) krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue); if (svalue == NULL) svalue = strdup(DEFAULT_ENCTYPE_LIST); params.keysalts = NULL; params.num_keysalts = 0; krb5_string_to_keysalts(svalue, ", \t",/* Tuple separators */ ":.-", /* Key/salt separators */ 0, /* No duplicates */ ¶ms.keysalts, ¶ms.num_keysalts); if (params.num_keysalts) params.mask |= KADM5_CONFIG_ENCTYPES; if (svalue) krb5_xfree(svalue); } hierarchy[2] = "kpasswd_server"; if (params_in->mask & KADM5_CONFIG_KPASSWD_SERVER) { params.mask |= KADM5_CONFIG_KPASSWD_SERVER; params.kpasswd_server = strdup(params_in->kpasswd_server); } else { svalue = NULL; if (aprofile) krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue); if (svalue == NULL) { #ifdef KRB5_DNS_LOOKUP if (strcmp(envname, "KRB5_CONFIG") == 0) { /* * Solaris Kerberos: only do DNS lookup for * kpasswd_server if this is a krb5.conf type of * config file. Note, the filename may not be * /etc/krb5/krb5.conf so we assume that the * KRB5_CONFIG envname string will consistently * indicate the type of config file. */ dnsret = krb5_get_servername(context, &dns_realm, "_kpasswd", "_udp", dns_host, &dns_portno); if (dnsret == 0) { params.kpasswd_server = strdup(dns_host); if (params.kpasswd_server) { params.mask |= KADM5_CONFIG_KPASSWD_SERVER; } params.kpasswd_port = dns_portno; params.mask |= KADM5_CONFIG_KPASSWD_PORT; } } #endif /* KRB5_DNS_LOOKUP */ /* * If a unique 'kpasswd_server' is not specified, * use the normal 'admin_server'. */ if ((params.mask & KADM5_CONFIG_ADMIN_SERVER) && dnsret) { params.kpasswd_server = strdup(params.admin_server); params.mask |= KADM5_CONFIG_KPASSWD_SERVER; } } else { char *p; params.kpasswd_server = svalue; params.mask |= KADM5_CONFIG_KPASSWD_SERVER; if ((p = strchr(params.kpasswd_server, ':'))) { params.kpasswd_port = atoi(p+1); params.mask |= KADM5_CONFIG_KPASSWD_PORT; *p = '\0'; } } } hierarchy[2] = "kpasswd_protocol"; /* default to current RPCSEC_GSS protocol */ params.kpasswd_protocol = KRB5_CHGPWD_RPCSEC; params.mask |= KADM5_CONFIG_KPASSWD_PROTOCOL; if (params_in->mask & KADM5_CONFIG_KPASSWD_PROTOCOL) { params.mask |= KADM5_CONFIG_KPASSWD_PROTOCOL; params.kpasswd_protocol = params_in->kpasswd_protocol; } else { svalue = NULL; if (aprofile) krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue); if (svalue != NULL) { if (strcasecmp(svalue, "RPCSEC_GSS") == 0) { params.kpasswd_protocol = KRB5_CHGPWD_RPCSEC; params.mask |= KADM5_CONFIG_KPASSWD_PROTOCOL; } else if (strcasecmp(svalue, "SET_CHANGE") == 0) { params.kpasswd_protocol = KRB5_CHGPWD_CHANGEPW_V2; params.mask |= KADM5_CONFIG_KPASSWD_PROTOCOL; } } if (svalue) krb5_xfree(svalue); } /* * If the kpasswd_port is not yet defined, define it now. */ if (! (params.mask & KADM5_CONFIG_KPASSWD_PORT)) { if (params_in->mask & KADM5_CONFIG_KPASSWD_PORT) params.kpasswd_port = params_in->kpasswd_port; /* * If kpasswd_port is not explicitly defined, * determine the port to use based on the protocol. * The alternative protocol uses a different port * than the standard admind port. */ else if (params.kpasswd_protocol == KRB5_CHGPWD_RPCSEC) { params.kpasswd_port = DEFAULT_KADM5_PORT; } else { /* * When using the Horowitz/IETF protocol for * password changing, the default port is 464 * (officially recognized by IANA). */ params.kpasswd_port = DEFAULT_KPASSWD_PORT; } params.mask |= KADM5_CONFIG_KPASSWD_PORT; } hierarchy[2] = "sunw_dbprop_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 { if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { if (strncasecmp(svalue, "Y", 1) == 0) params.iprop_enabled = TRUE; if (strncasecmp(svalue, "true", 4) == 0) params.iprop_enabled = TRUE; params.mask |= KADM5_CONFIG_IPROP_ENABLED; krb5_xfree(svalue); } } hierarchy[2] = "sunw_dbprop_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; } } hierarchy[2] = "sunw_dbprop_slave_poll"; params.iprop_polltime = strdup("2m"); if (params.iprop_polltime) params.mask |= KADM5_CONFIG_POLL_TIME; if (params_in->mask & KADM5_CONFIG_POLL_TIME) { if (params.iprop_polltime) free(params.iprop_polltime); params.iprop_polltime = strdup(params_in->iprop_polltime); if (params.iprop_polltime) params.mask |= KADM5_CONFIG_POLL_TIME; } else { if (aprofile && !krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { if (params.iprop_polltime) free(params.iprop_polltime); params.iprop_polltime = strdup(svalue); params.mask |= KADM5_CONFIG_POLL_TIME; krb5_xfree(svalue); } } *params_out = params; cleanup: if (aprofile) krb5_aprof_finish(aprofile); if (kret) { kadm5_free_config_params(context, ¶ms); params_out->mask = 0; } #ifdef KRB5_DNS_LOOKUP if (dns_realm.data) free(dns_realm.data); #endif /* KRB5_DNS_LOOKUP */ return(kret); } /* * kadm5_free_config_params() - Free data allocated by above. */ /*ARGSUSED*/ krb5_error_code kadm5_free_config_params(context, params) krb5_context context; kadm5_config_params *params; { if (params) { if (params->dbname) { krb5_xfree(params->dbname); params->dbname = NULL; } if (params->mkey_name) { krb5_xfree(params->mkey_name); params->mkey_name = NULL; } if (params->stash_file) { krb5_xfree(params->stash_file); params->stash_file = NULL; } if (params->keysalts) { krb5_xfree(params->keysalts); params->keysalts = NULL; params->num_keysalts = 0; } if (params->admin_keytab) { free(params->admin_keytab); params->admin_keytab = NULL; } if (params->dict_file) { free(params->dict_file); params->dict_file = NULL; } if (params->acl_file) { free(params->acl_file); params->acl_file = NULL; } if (params->realm) { free(params->realm); params->realm = NULL; } if (params->admin_dbname) { free(params->admin_dbname); params->admin_dbname = NULL; } if (params->admin_lockfile) { free(params->admin_lockfile); params->admin_lockfile = NULL; } if (params->admin_server) { free(params->admin_server); params->admin_server = NULL; } if (params->kpasswd_server) { free(params->kpasswd_server); params->kpasswd_server = NULL; } if (params->iprop_polltime) { free(params->iprop_polltime); params->iprop_polltime = NULL; } } return (0); } krb5_error_code kadm5_get_admin_service_name(krb5_context ctx, char *realm_in, char *admin_name, size_t maxlen) { krb5_error_code ret; kadm5_config_params params_in, params_out; struct hostent *hp; memset(¶ms_in, 0, sizeof(params_in)); memset(¶ms_out, 0, sizeof(params_out)); params_in.mask |= KADM5_CONFIG_REALM; params_in.realm = realm_in; ret = kadm5_get_config_params(ctx, 0, ¶ms_in, ¶ms_out); if (ret) return ret; if (!(params_out.mask & KADM5_CONFIG_ADMIN_SERVER)) { ret = KADM5_MISSING_KRB5_CONF_PARAMS; goto err_params; } hp = gethostbyname(params_out.admin_server); if (hp == NULL) { ret = errno; goto err_params; } if (strlen(hp->h_name) + sizeof("kadmin/") > maxlen) { ret = ENOMEM; goto err_params; } sprintf(admin_name, "kadmin/%s", hp->h_name); err_params: kadm5_free_config_params(ctx, ¶ms_out); return ret; } /*********************************************************************** * This is the old krb5_realm_read_params, which I mutated into * kadm5_get_config_params but which old code (kdb5_* and krb5kdc) * still uses. ***********************************************************************/ /* * krb5_read_realm_params() - Read per-realm parameters from KDC * alternate profile. */ krb5_error_code krb5_read_realm_params(kcontext, realm, rparamp) krb5_context kcontext; char *realm; krb5_realm_params **rparamp; { char *filename; char *envname; char *lrealm; krb5_pointer aprofile = 0; krb5_realm_params *rparams; const char *hierarchy[4]; char *svalue; krb5_int32 ivalue; krb5_boolean bvalue; krb5_deltat dtvalue; char *kdcprofile = 0; char *kdcenv = 0; krb5_error_code kret; filename = (kdcprofile) ? kdcprofile : DEFAULT_KDC_PROFILE; envname = (kdcenv) ? kdcenv : KDC_PROFILE_ENV; if (kcontext->profile_secure == TRUE) envname = 0; rparams = (krb5_realm_params *) NULL; if (realm) lrealm = strdup(realm); else { kret = krb5_get_default_realm(kcontext, &lrealm); if (kret) goto cleanup; } kret = krb5_aprof_init(filename, envname, &aprofile); if (kret) goto cleanup; rparams = (krb5_realm_params *) malloc(sizeof(krb5_realm_params)); if (rparams == 0) { kret = ENOMEM; goto cleanup; } /* Initialize realm parameters */ memset((char *) rparams, 0, sizeof(krb5_realm_params)); /* Get the value for the database */ hierarchy[0] = "realms"; hierarchy[1] = lrealm; hierarchy[2] = "database_name"; hierarchy[3] = (char *) NULL; if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) rparams->realm_dbname = svalue; /* Get the value for the KDC port list */ hierarchy[2] = "kdc_ports"; if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) rparams->realm_kdc_ports = svalue; hierarchy[2] = "kdc_tcp_ports"; if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) rparams->realm_kdc_tcp_ports = svalue; /* Get the name of the acl file */ hierarchy[2] = "acl_file"; if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) rparams->realm_acl_file = svalue; /* Get the value for the kadmind port */ hierarchy[2] = "kadmind_port"; if (!krb5_aprof_get_int32(aprofile, hierarchy, TRUE, &ivalue)) { rparams->realm_kadmind_port = ivalue; rparams->realm_kadmind_port_valid = 1; } /* Get the value for the master key name */ hierarchy[2] = "master_key_name"; if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) rparams->realm_mkey_name = svalue; /* Get the value for the master key type */ hierarchy[2] = "master_key_type"; if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { if (!krb5_string_to_enctype(svalue, &rparams->realm_enctype)) rparams->realm_enctype_valid = 1; krb5_xfree(svalue); } /* Get the value for the stashfile */ hierarchy[2] = "key_stash_file"; if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) rparams->realm_stash_file = svalue; /* Get the value for maximum ticket lifetime. */ hierarchy[2] = "max_life"; if (!krb5_aprof_get_deltat(aprofile, hierarchy, TRUE, &dtvalue)) { rparams->realm_max_life = dtvalue; rparams->realm_max_life_valid = 1; } /* Get the value for maximum renewable ticket lifetime. */ hierarchy[2] = "max_renewable_life"; if (!krb5_aprof_get_deltat(aprofile, hierarchy, TRUE, &dtvalue)) { rparams->realm_max_rlife = dtvalue; rparams->realm_max_rlife_valid = 1; } /* Get the value for the default principal expiration */ hierarchy[2] = "default_principal_expiration"; if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { if (!krb5_string_to_timestamp(svalue, &rparams->realm_expiration)) rparams->realm_expiration_valid = 1; krb5_xfree(svalue); } hierarchy[2] = "reject_bad_transit"; if (!krb5_aprof_get_boolean(aprofile, hierarchy, TRUE, &bvalue)) { rparams->realm_reject_bad_transit = bvalue; rparams->realm_reject_bad_transit_valid = 1; } /* Get the value for the default principal flags */ hierarchy[2] = "default_principal_flags"; if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { char *sp, *ep, *tp; sp = svalue; rparams->realm_flags = 0; while (sp) { if ((ep = strchr(sp, (int) ',')) || (ep = strchr(sp, (int) ' ')) || (ep = strchr(sp, (int) '\t'))) { /* Fill in trailing whitespace of sp */ tp = ep - 1; while (isspace((int) *tp) && (tp < sp)) { *tp = '\0'; tp--; } *ep = '\0'; ep++; /* Skip over trailing whitespace of ep */ while (isspace((int) *ep) && (*ep)) ep++; } /* Convert this flag */ if (krb5_string_to_flags(sp, "+", "-", &rparams->realm_flags)) break; sp = ep; } if (!sp) rparams->realm_flags_valid = 1; krb5_xfree(svalue); } /* Get the value for the supported enctype/salttype matrix */ /* * SUNWresync121 * Solaris kerberos: updated this code to support default values for * the supported_enctypes. */ hierarchy[2] = "supported_enctypes"; svalue = NULL; krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue); /* * Set the default value if supported_enctypes was not explicitly * set in the kdc.conf. */ if (svalue == NULL) { svalue = strdup(DEFAULT_ENCTYPE_LIST); } if (svalue != NULL) { krb5_string_to_keysalts(svalue, ", \t", /* Tuple separators */ ":.-", /* Key/salt separators */ 0, /* No duplicates */ &rparams->realm_keysalts, &rparams->realm_num_keysalts); krb5_xfree(svalue); svalue = NULL; } cleanup: if (aprofile) krb5_aprof_finish(aprofile); if (lrealm) free(lrealm); if (kret) { if (rparams) krb5_free_realm_params(kcontext, rparams); rparams = 0; } *rparamp = rparams; return(kret); } /* * krb5_free_realm_params() - Free data allocated by above. */ krb5_error_code krb5_free_realm_params(kcontext, rparams) krb5_context kcontext; krb5_realm_params *rparams; { if (rparams) { if (rparams->realm_profile) krb5_xfree(rparams->realm_profile); if (rparams->realm_dbname) krb5_xfree(rparams->realm_dbname); if (rparams->realm_mkey_name) krb5_xfree(rparams->realm_mkey_name); if (rparams->realm_stash_file) krb5_xfree(rparams->realm_stash_file); if (rparams->realm_keysalts) krb5_xfree(rparams->realm_keysalts); if (rparams->realm_kdc_ports) krb5_xfree(rparams->realm_kdc_ports); if (rparams->realm_kdc_tcp_ports) krb5_xfree(rparams->realm_kdc_tcp_ports); if (rparams->realm_acl_file) krb5_xfree(rparams->realm_acl_file); krb5_xfree(rparams); } return(0); } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. */ #include #ifdef HAVE_MEMORY_H #include #endif #include #include #include #include "admin_internal.h" #include #include #define string_text error_message const char *chpw_error_message(kadm5_ret_t code); /* * Function: kadm5_chpass_principal_util * * Purpose: Wrapper around chpass_principal. We can read new pw, change pw and return useful messages * * Arguments: * * princ (input) a krb5b_principal structure for the * principal whose password we should change. * * new_password (input) NULL or a null terminated string with the * the principal's desired new password. If new_password * is NULL then this routine will read a new password. * * pw_ret (output) if non-NULL, points to a static buffer * containing the new password (if password is prompted * internally), or to the new_password argument (if * that is non-NULL). If the former, then the buffer * is only valid until the next call to the function, * and the caller should be sure to zero it when * it is no longer needed. * * msg_ret (output) a useful message is copied here. * * exit status of 0 for success, else the com err code * for the last significant routine called. * * Requires: * * A msg_ret should point to a buffer large enough for the messasge. * * Effects: * * Modifies: * * */ kadm5_ret_t _kadm5_chpass_principal_util(void *server_handle, void *lhandle, krb5_principal princ, char *new_pw, char **ret_pw, char *msg_ret, unsigned int msg_len) { int code, code2; unsigned int pwsize; static char buffer[255]; char *new_password; kadm5_principal_ent_rec princ_ent; kadm5_policy_ent_rec policy_ent; krb5_chgpwd_prot passwd_protocol; _KADM5_CHECK_HANDLE(server_handle); if (ret_pw) *ret_pw = NULL; if (new_pw != NULL) { new_password = new_pw; } else { /* read the password */ krb5_context context; if ((code = (int) kadm5_init_krb5_context(&context)) == 0) { pwsize = sizeof(buffer); code = krb5_read_password(context, KADM5_PW_FIRST_PROMPT, KADM5_PW_SECOND_PROMPT, buffer, &pwsize); krb5_free_context(context); } if (code == 0) new_password = buffer; else { #ifdef ZEROPASSWD memset(buffer, 0, sizeof(buffer)); #endif if (code == KRB5_LIBOS_BADPWDMATCH) { (void) strncpy(msg_ret, string_text(CHPASS_UTIL_NEW_PASSWORD_MISMATCH), msg_len - 1); msg_ret[msg_len - 1] = '\0'; return(code); } else { (void) strncpy(msg_ret, error_message(code), msg_len - 1); (void) strncat(msg_ret, " ", msg_len - 1); (void) strncat(msg_ret, string_text(CHPASS_UTIL_WHILE_READING_PASSWORD), msg_len - 1); (void) strncat(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), msg_len - 1); msg_ret[msg_len - 1] = '\0'; return(code); } } if (pwsize == 0) { #ifdef ZEROPASSWD memset(buffer, 0, sizeof(buffer)); #endif strncpy(msg_ret, string_text(CHPASS_UTIL_NO_PASSWORD_READ), msg_len - 1); msg_ret[msg_len - 1] = '\0'; return(KRB5_LIBOS_CANTREADPWD); /* could do better */ } } if (ret_pw) *ret_pw = new_password; passwd_protocol = _kadm5_get_kpasswd_protocol(server_handle); if (passwd_protocol == KRB5_CHGPWD_CHANGEPW_V2) { kadm5_ret_t srvr_rsp_code; krb5_data srvr_msg; srvr_msg.length = 0; srvr_msg.data = NULL; code = kadm5_chpass_principal_v2(server_handle, princ, new_password, &srvr_rsp_code, &srvr_msg); if (srvr_rsp_code) { sprintf(msg_ret, "%s%s%.*s\n", chpw_error_message(srvr_rsp_code), srvr_msg.length? ": " : "", srvr_msg.length, srvr_msg.data ? srvr_msg.data : ""); return (srvr_rsp_code); } return (code); } else if (passwd_protocol == KRB5_CHGPWD_RPCSEC) { code = kadm5_chpass_principal(server_handle, princ, new_password); #ifdef ZEROPASSWD if (!ret_pw) memset(buffer, 0, sizeof(buffer)); /* in case we read a new password */ #endif if (code == KADM5_OK) { strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_CHANGED), msg_len - 1); msg_ret[msg_len - 1] = '\0'; return(0); } if ((code != KADM5_PASS_Q_TOOSHORT) && (code != KADM5_PASS_REUSE) &&(code != KADM5_PASS_Q_CLASS) && (code != KADM5_PASS_Q_DICT) && (code != KADM5_PASS_TOOSOON)) { /* Can't get more info for other errors */ sprintf(buffer, "%s %s", error_message(code), string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE)); sprintf(msg_ret, "%s\n%s\n", string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), buffer); return(code); } /* Ok, we have a password quality error. Return a good message */ if (code == KADM5_PASS_REUSE) { strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_REUSE), msg_len - 1); msg_ret[msg_len - 1] = '\0'; return(code); } if (code == KADM5_PASS_Q_DICT) { strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_IN_DICTIONARY), msg_len - 1); msg_ret[msg_len - 1] = '\0'; return(code); } /* Look up policy for the remaining messages */ code2 = kadm5_get_principal (lhandle, princ, &princ_ent, KADM5_PRINCIPAL_NORMAL_MASK); if (code2 != 0) { strncpy(msg_ret, error_message(code2), msg_len - 1); strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret)); strncat(msg_ret, string_text(CHPASS_UTIL_GET_PRINC_INFO), msg_len - 1 - strlen(msg_ret)); strncat(msg_ret, "\n", msg_len - 1 - strlen(msg_ret)); strncat(msg_ret, error_message(code), msg_len - 1 - strlen(msg_ret)); strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret)); strncat(msg_ret, string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE), msg_len - 1 - strlen(msg_ret)); strncat(msg_ret, "\n\n", msg_len - 1 - strlen(msg_ret)); strncat(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), msg_len - 1 - strlen(msg_ret)); strncat(msg_ret, "\n", msg_len - 1 - strlen(msg_ret)); msg_ret[msg_len - 1] = '\0'; return(code); } if ((princ_ent.aux_attributes & KADM5_POLICY) == 0) { strncpy(msg_ret, error_message(code), msg_len - 1 - strlen(msg_ret)); strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret)); strncpy(msg_ret, string_text(CHPASS_UTIL_NO_POLICY_YET_Q_ERROR), msg_len - 1 - strlen(msg_ret)); strncat(msg_ret, "\n\n", msg_len - 1 - strlen(msg_ret)); strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), msg_len - 1 - strlen(msg_ret)); msg_ret[msg_len - 1] = '\0'; (void) kadm5_free_principal_ent(lhandle, &princ_ent); return(code); } code2 = kadm5_get_policy(lhandle, princ_ent.policy, &policy_ent); if (code2 != 0) { sprintf(msg_ret, "%s %s\n%s %s\n\n%s\n ", error_message(code2), string_text(CHPASS_UTIL_GET_POLICY_INFO), error_message(code), string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE), string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED)); (void) kadm5_free_principal_ent(lhandle, &princ_ent); return(code); } if (code == KADM5_PASS_Q_TOOSHORT) { sprintf(msg_ret, string_text(CHPASS_UTIL_PASSWORD_TOO_SHORT), policy_ent.pw_min_length); (void) kadm5_free_principal_ent(lhandle, &princ_ent); (void) kadm5_free_policy_ent(lhandle, &policy_ent); return(code); } if (code == KADM5_PASS_Q_CLASS) { sprintf(msg_ret, string_text(CHPASS_UTIL_TOO_FEW_CLASSES), policy_ent.pw_min_classes); (void) kadm5_free_principal_ent(lhandle, &princ_ent); (void) kadm5_free_policy_ent(lhandle, &policy_ent); return(code); } if (code == KADM5_PASS_TOOSOON) { time_t until; char *time_string, *ptr; until = princ_ent.last_pwd_change + policy_ent.pw_min_life; time_string = ctime(&until); if (*(ptr = &time_string[strlen(time_string)-1]) == '\n') *ptr = '\0'; sprintf(msg_ret, string_text(CHPASS_UTIL_PASSWORD_TOO_SOON), time_string); (void) kadm5_free_principal_ent(lhandle, &princ_ent); (void) kadm5_free_policy_ent(lhandle, &policy_ent); return(code); } else { /* We should never get here, but just in case ... */ sprintf(buffer, "%s %s", error_message(code), string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE)); sprintf(msg_ret, "%s\n%s\n", string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), buffer); (void) kadm5_free_principal_ent(lhandle, &princ_ent); (void) kadm5_free_policy_ent(lhandle, &policy_ent); return(code); } } else { sprintf(msg_ret, "%s\n%s\n", string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), "Password protocol in krb5.conf is not supported\n"); return (-1); } } /* * krb5_chpw_result_code_string * * convert the return code received from the password server * to a human-readable string. */ const char * chpw_error_message(kadm5_ret_t result_code) { switch (result_code) { case KRB5_KPASSWD_MALFORMED: return (dgettext(TEXT_DOMAIN, "Malformed request error")); case KRB5_KPASSWD_HARDERROR: return (dgettext(TEXT_DOMAIN, "Server error")); case KRB5_KPASSWD_AUTHERROR: return (dgettext(TEXT_DOMAIN, "Authentication error")); case KRB5_KPASSWD_SOFTERROR: return (dgettext(TEXT_DOMAIN, "Password change rejected")); case KRB5_KPASSWD_ACCESSDENIED: return (dgettext(TEXT_DOMAIN, "Not authorized to change password")); case KRB5_KPASSWD_BAD_VERSION: return (dgettext(TEXT_DOMAIN, "Protocol version unsupported")); case KRB5_KPASSWD_INITIAL_FLAG_NEEDED: return (dgettext(TEXT_DOMAIN, "initial flag required in changepw request")); case KRB5_KPASSWD_POLICY_REJECT: return (dgettext(TEXT_DOMAIN, "new password fails policy")); case KRB5_KPASSWD_BAD_PRINCIPAL: return (dgettext(TEXT_DOMAIN, "target principal does not exist for " "changepw request")); case KRB5_KPASSWD_ETYPE_NOSUPP: return (dgettext(TEXT_DOMAIN, "changepw request key sequence has an " "unsupported Etype")); default: return (dgettext(TEXT_DOMAIN, "Password change failed")); } } /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * chpass_util_strings.h: * This file is automatically generated; please do not edit it. */ #define CHPASS_UTIL_GET_POLICY_INFO (-1492553984L) #define CHPASS_UTIL_GET_PRINC_INFO (-1492553983L) #define CHPASS_UTIL_NEW_PASSWORD_MISMATCH (-1492553982L) #define CHPASS_UTIL_NEW_PASSWORD_PROMPT (-1492553981L) #define CHPASS_UTIL_NEW_PASSWORD_AGAIN_PROMPT (-1492553980L) #define CHPASS_UTIL_NO_PASSWORD_READ (-1492553979L) #define CHPASS_UTIL_NO_POLICY_YET_Q_ERROR (-1492553978L) #define CHPASS_UTIL_PASSWORD_CHANGED (-1492553977L) #define CHPASS_UTIL_PASSWORD_IN_DICTIONARY (-1492553976L) #define CHPASS_UTIL_PASSWORD_NOT_CHANGED (-1492553975L) #define CHPASS_UTIL_PASSWORD_TOO_SHORT (-1492553974L) #define CHPASS_UTIL_TOO_FEW_CLASSES (-1492553973L) #define CHPASS_UTIL_PASSWORD_TOO_SOON (-1492553972L) #define CHPASS_UTIL_PASSWORD_REUSE (-1492553971L) #define CHPASS_UTIL_WHILE_TRYING_TO_CHANGE (-1492553970L) #define CHPASS_UTIL_WHILE_READING_PASSWORD (-1492553969L) #define ERROR_TABLE_BASE_ovku (-1492553984L) extern const struct error_table et_ovku_error_table; #if !defined(_WIN32) /* for compatibility with older versions... */ extern void initialize_ovku_error_table (void) /*@modifies internalState@*/; #else #define initialize_ovku_error_table() #endif #if !defined(_WIN32) #define init_ovku_err_tbl initialize_ovku_error_table #define ovku_err_base ERROR_TABLE_BASE_ovku #endif # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include ../../Makefile.lib # Hammerhead: amd64-only SUBDIRS = $(MACH64) all : TARGET= all clean : TARGET= clean clobber : TARGET= clobber install : TARGET= install LIBRARY= libkadm5clnt.a POFILE= $(LIBRARY:%.a=%.po) POFILES= generic.po .KEEP_STATE: all clean install: $(SUBDIRS) clobber: $(SUBDIRS) $(RM) $(CLOBBERFILES) _msg: $(MSGDOMAIN) .WAIT $(POFILE) $(RM) $(MSGDOMAIN)/$(POFILE) $(CP) $(POFILE) $(MSGDOMAIN) $(POFILE): $(DERIVED_FILES) .WAIT $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ generic.po: FRC $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext ../*.[ch] *.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: $(MSGDOMAIN): $(INS.dir) # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. LIBRARY= libkadm5clnt.a VERS= .1 CLNT_OBJS = clnt_policy.o \ client_rpc.o \ client_principal.o \ client_init.o \ clnt_privs.o \ clnt_chpass_util.o \ logger.o \ changepw.o \ chpw.o SHARED_OBJS = \ alt_prof.o \ chpass_util.o \ kadm_rpc_xdr.o \ misc_free.o \ kadm_host_srv_names.o \ str_conv.o OBJECTS= $(CLNT_OBJS) $(SHARED_OBJS) ISRCHDR= ../iprop.h KRB5IPROPDIR= $(SRC)/cmd/krb5/iprop # include library definitions include ../../../Makefile.lib SRCS= $(CLNT_OBJS:%.o=../%.c) \ $(SHARED_OBJS:%.o=../../%.c) LIBS= $(DYNLIB) include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 POFILE = $(LIBRARY:%.a=%.po) POFILES = generic.po #override liblink INS.liblink= -$(RM) $@; $(SYMLINK) $(LIBLINKS)$(VERS) $@ CPPFLAGS += -I.. -I../.. -I../../.. -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/lib/krb5 \ -I$(SRC)/lib/gss_mechs/mech_krb5/include/krb5 \ -I$(SRC)/uts/common/gssapi/ \ -I$(SRC)/uts/common/gssapi/include/ \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \ -I$(SRC)/lib/gss_mechs/mech_krb5/krb5/os \ -I$(KRB5IPROPDIR) \ -DHAVE_STDLIB_H -DUSE_SOLARIS_SHARED_LIBRARIES \ -DHAVE_LIBSOCKET=1 -DHAVE_LIBNSL=1 -DSETRPCENT_TYPE=void \ -DENDRPCENT_TYPE=void -DHAVE_SYS_ERRLIST=1 -DNEED_SYS_ERRLIST=1 \ -DHAVE_SYSLOG_H=1 -DHAVE_OPENLOG=1 -DHAVE_SYSLOG=1 -DHAVE_CLOSELOG=1 \ -DHAVE_STRFTIME=1 -DHAVE_VSPRINTF=1 -DUSE_KADM5_API_VERSION=2 # Hammerhead: -I.. already in CPPFLAGS above; removed from CFLAGS (lost in Makefile.master.64 reset) CFLAGS += $(CCVERBOSE) CERRWARN += -Wno-unused-function CERRWARN += -Wno-unused-variable CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) SMOFF += all_func_returns,indenting,no_if_block LDLIBS += -lc .KEEP_STATE: all: $(LIBS) # Hammerhead: pre-generated (rpcgen + GNU cpp truncation bug) # iprop.h is now a committed source file. # Hammerhead: GNU Make parallel builds need PICS to depend on generated files. # $(LIBS): $(ISRCHDR) only ensures iprop.h before link, not before .o compilation. $(PICS): $(ISRCHDR) # Hammerhead: pre-generated files are permanent source, do not delete # CLEANFILES += $(ISRCHDR) # include library targets include ../../../Makefile.targ pics/%.o: ../../%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) FRC: generic.po: FRC $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext ../*.[ch] ../../*.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" # include ../Makefile.com include ../../../../Makefile.lib.64 DYNFLAGS += $(KRUNPATH64) $(KMECHLIB64) LDLIBS += -L $(ROOTLIBDIR) -lgss -lnsl -lsocket -lc install: all $(ROOTLIBS64) $(ROOTLINKS64) /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * lib/krb5/os/changepw.c * * Copyright 1990,1999 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ #define NEED_SOCKETS #include #include #include #include #include #include #include /* #include "adm_err.h" */ #include #include extern krb5_error_code krb5int_mk_chpw_req(krb5_context context, krb5_auth_context auth_context, krb5_data *ap_req, char *passwd, krb5_data *packet); extern krb5_error_code krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data); /* * _kadm5_get_kpasswd_protocol * * returns the password change protocol value to the caller. * Since the 'handle' is an opaque value to higher up callers, * this method is needed to provide a way for them to get a peek * at the protocol being used without having to expose the entire * handle structure. */ krb5_chgpwd_prot _kadm5_get_kpasswd_protocol(void *handle) { kadm5_server_handle_t srvrhdl = (kadm5_server_handle_t)handle; return (srvrhdl->params.kpasswd_protocol); } /* * krb5_change_password * * Prepare and send a CHANGEPW request to a password server * using UDP datagrams. This is only used for sending to * non-SEAM servers which support the Marc Horowitz defined * protocol (1998) for password changing. * * SUNW14resync - added _local as it conflicts with one in krb5.h */ static krb5_error_code krb5_change_password_local(context, params, creds, newpw, srvr_rsp_code, srvr_msg) krb5_context context; kadm5_config_params *params; krb5_creds *creds; char *newpw; kadm5_ret_t *srvr_rsp_code; krb5_data *srvr_msg; { krb5_auth_context auth_context; krb5_data ap_req, chpw_req, chpw_rep; krb5_address local_kaddr, remote_kaddr; krb5_error_code code = 0; int i, addrlen; struct sockaddr *addr_p, local_addr, remote_addr, tmp_addr; struct sockaddr_in *sin_p; struct hostent *hp; int naddr_p; int cc, local_result_code, tmp_len; SOCKET s1 = INVALID_SOCKET; SOCKET s2 = INVALID_SOCKET; /* Initialize values so that cleanup call can safely check for NULL */ auth_context = NULL; addr_p = NULL; memset(&chpw_req, 0, sizeof (krb5_data)); memset(&chpw_rep, 0, sizeof (krb5_data)); memset(&ap_req, 0, sizeof (krb5_data)); /* initialize auth_context so that we know we have to free it */ if ((code = krb5_auth_con_init(context, &auth_context))) goto cleanup; if (code = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY, NULL, creds, &ap_req)) goto cleanup; /* * find the address of the kpasswd_server. */ addr_p = (struct sockaddr *)malloc(sizeof (struct sockaddr)); if (!addr_p) goto cleanup; memset(addr_p, 0, sizeof (struct sockaddr)); if ((hp = gethostbyname(params->kpasswd_server)) == NULL) { code = KRB5_REALM_CANT_RESOLVE; goto cleanup; } sin_p = (struct sockaddr_in *)addr_p; memset((char *)sin_p, 0, sizeof (struct sockaddr)); sin_p->sin_family = hp->h_addrtype; sin_p->sin_port = htons(params->kpasswd_port); memcpy((char *)&sin_p->sin_addr, (char *)hp->h_addr, hp->h_length); naddr_p = 1; /* * this is really obscure. s1 is used for all communications. it * is left unconnected in case the server is multihomed and routes * are asymmetric. s2 is connected to resolve routes and get * addresses. this is the *only* way to get proper addresses for * multihomed hosts if routing is asymmetric. * * A related problem in the server, but not the client, is that * many os's have no way to disconnect a connected udp socket, so * the s2 socket needs to be closed and recreated for each * request. The s1 socket must not be closed, or else queued * requests will be lost. * * A "naive" client implementation (one socket, no connect, * hostname resolution to get the local ip addr) will work and * interoperate if the client is single-homed. */ if ((s1 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) { code = errno; goto cleanup; } if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) { code = errno; goto cleanup; } for (i = 0; i < naddr_p; i++) { fd_set fdset; struct timeval timeout; if (connect(s2, &addr_p[i], sizeof (addr_p[i])) == SOCKET_ERROR) { if ((errno == ECONNREFUSED) || (errno == EHOSTUNREACH)) continue; /* try the next addr */ code = errno; goto cleanup; } addrlen = sizeof (local_addr); if (getsockname(s2, &local_addr, &addrlen) < 0) { if ((errno == ECONNREFUSED) || (errno == EHOSTUNREACH)) continue; /* try the next addr */ code = errno; goto cleanup; } /* * some brain-dead OS's don't return useful information from * the getsockname call. Namely, windows and solaris. */ if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) { local_kaddr.addrtype = ADDRTYPE_INET; local_kaddr.length = sizeof (((struct sockaddr_in *) &local_addr)->sin_addr); local_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *) &local_addr)->sin_addr); } else { krb5_address **addrs; krb5_os_localaddr(context, &addrs); local_kaddr.magic = addrs[0]->magic; local_kaddr.addrtype = addrs[0]->addrtype; local_kaddr.length = addrs[0]->length; local_kaddr.contents = malloc(addrs[0]->length); memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length); krb5_free_addresses(context, addrs); } addrlen = sizeof (remote_addr); if (getpeername(s2, &remote_addr, &addrlen) < 0) { if ((errno == ECONNREFUSED) || (errno == EHOSTUNREACH)) continue; /* try the next addr */ code = errno; goto cleanup; } remote_kaddr.addrtype = ADDRTYPE_INET; remote_kaddr.length = sizeof (((struct sockaddr_in *) &remote_addr)->sin_addr); remote_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&remote_addr)->sin_addr); /* * mk_priv requires that the local address be set. * getsockname is used for this. rd_priv requires that the * remote address be set. recvfrom is used for this. If * rd_priv is given a local address, and the message has the * recipient addr in it, this will be checked. However, there * is simply no way to know ahead of time what address the * message will be delivered *to*. Therefore, it is important * that either no recipient address is in the messages when * mk_priv is called, or that no local address is passed to * rd_priv. Both is a better idea, and I have done that. In * summary, when mk_priv is called, *only* a local address is * specified. when rd_priv is called, *only* a remote address * is specified. Are we having fun yet? */ if (code = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL)) { code = errno; goto cleanup; } if (code = krb5int_mk_chpw_req(context, auth_context, &ap_req, newpw, &chpw_req)) { code = errno; goto cleanup; } if ((cc = sendto(s1, chpw_req.data, chpw_req.length, 0, (struct sockaddr *)&addr_p[i], sizeof (addr_p[i]))) != chpw_req.length) { if ((cc < 0) && ((errno == ECONNREFUSED) || (errno == EHOSTUNREACH))) continue; /* try the next addr */ code = (cc < 0) ? errno : ECONNABORTED; goto cleanup; } chpw_rep.length = 1500; chpw_rep.data = (char *)malloc(chpw_rep.length); /* XXX need a timeout/retry loop here */ FD_ZERO(&fdset); FD_SET(s1, &fdset); timeout.tv_sec = 120; timeout.tv_usec = 0; switch (select(s1 + 1, &fdset, 0, 0, &timeout)) { case -1: code = errno; goto cleanup; case 0: code = ETIMEDOUT; goto cleanup; default: /* fall through */ ; } tmp_len = sizeof (tmp_addr); if ((cc = recvfrom(s1, chpw_rep.data, chpw_rep.length, 0, &tmp_addr, &tmp_len)) < 0) { code = errno; goto cleanup; } closesocket(s1); s1 = INVALID_SOCKET; closesocket(s2); s2 = INVALID_SOCKET; chpw_rep.length = cc; if (code = krb5_auth_con_setaddrs(context, auth_context, NULL, &remote_kaddr)) goto cleanup; if (code = krb5int_rd_chpw_rep(context, auth_context, &chpw_rep, &local_result_code, srvr_msg)) goto cleanup; if (srvr_rsp_code) *srvr_rsp_code = local_result_code; code = 0; goto cleanup; } code = errno; cleanup: if (auth_context != NULL) krb5_auth_con_free(context, auth_context); if (addr_p != NULL) krb5_xfree(addr_p); if (s1 != INVALID_SOCKET) closesocket(s1); if (s2 != INVALID_SOCKET) closesocket(s2); krb5_xfree(chpw_req.data); krb5_xfree(chpw_rep.data); krb5_xfree(ap_req.data); return (code); } /* * kadm5_chpass_principal_v2 * * New function used to prepare to make the change password request to a * non-SEAM admin server. The protocol used in this case is not based on * RPCSEC_GSS, it simply makes the request to port 464 (udp and tcp). * This is the same way that MIT KRB5 1.2.1 changes passwords. */ kadm5_ret_t kadm5_chpass_principal_v2(void *server_handle, krb5_principal princ, char *newpw, kadm5_ret_t *srvr_rsp_code, krb5_data *srvr_msg) { kadm5_ret_t code; kadm5_server_handle_t handle = (kadm5_server_handle_t)server_handle; krb5_error_code result; krb5_creds mcreds; krb5_creds ncreds; krb5_ccache ccache; int cpwlen; char *cpw_service = NULL; /* * The credentials have already been stored in the cache in the * initialization step earlier, but we dont have direct access to it * at this level. Derive the cache and fetch the credentials to use for * sending the request. */ memset(&mcreds, 0, sizeof (krb5_creds)); if ((code = krb5_cc_resolve(handle->context, handle->cache_name, &ccache))) return (code); /* set the client principal in the credential match structure */ mcreds.client = princ; /* * set the server principal (kadmin/changepw@REALM) in the credential * match struct */ cpwlen = strlen(KADM5_CHANGEPW_SERVICE) + strlen(handle->params.realm) + 2; cpw_service = malloc(cpwlen); if (cpw_service == NULL) { return (ENOMEM); } snprintf(cpw_service, cpwlen, "%s@%s", KADM5_CHANGEPW_SERVICE, handle->params.realm); /* generate the server principal from the name string we generated */ if ((code = krb5_parse_name(handle->context, cpw_service, &mcreds.server))) { free(cpw_service); return (code); } /* Find the credentials in the cache */ if ((code = krb5_cc_retrieve_cred(handle->context, ccache, 0, &mcreds, &ncreds))) { free(cpw_service); return (code); } /* Now we have all we need to make the change request. */ result = krb5_change_password_local(handle->context, &handle->params, &ncreds, newpw, srvr_rsp_code, srvr_msg); free(cpw_service); return (result); } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include "k5-int.h" #include #include #include "auth_con.h" #include krb5_error_code krb5int_mk_chpw_req( krb5_context context, krb5_auth_context auth_context, krb5_data *ap_req, char *passwd, krb5_data *packet) { krb5_error_code ret = 0; krb5_data clearpw; krb5_data cipherpw; krb5_replay_data replay; char *ptr; cipherpw.data = NULL; if ((ret = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE))) goto cleanup; clearpw.length = strlen(passwd); clearpw.data = passwd; if ((ret = krb5_mk_priv(context, auth_context, &clearpw, &cipherpw, &replay))) goto cleanup; packet->length = 6 + ap_req->length + cipherpw.length; packet->data = (char *) malloc(packet->length); if (packet->data == NULL) { ret = ENOMEM; goto cleanup; } ptr = packet->data; /* length */ *ptr++ = (packet->length>> 8) & 0xff; *ptr++ = packet->length & 0xff; /* version == 0x0001 big-endian * NOTE: when MS and MIT start supporting the latest * version of the passwd change protocol (v2), * this value will change to 2. */ *ptr++ = 0; *ptr++ = 1; /* ap_req length, big-endian */ *ptr++ = (ap_req->length>>8) & 0xff; *ptr++ = ap_req->length & 0xff; /* ap-req data */ memcpy(ptr, ap_req->data, ap_req->length); ptr += ap_req->length; /* krb-priv of password */ memcpy(ptr, cipherpw.data, cipherpw.length); cleanup: if(cipherpw.data != NULL) /* allocated by krb5_mk_priv */ free(cipherpw.data); return(ret); } krb5_error_code krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data) { char *ptr; int plen, vno; krb5_data ap_rep; krb5_ap_rep_enc_part *ap_rep_enc; krb5_error_code ret; krb5_data cipherresult; krb5_data clearresult; krb5_error *krberror; krb5_replay_data replay; krb5_keyblock *tmp; int local_result_code; if (packet->length < 4) /* either this, or the server is printing bad messages, or the caller passed in garbage */ return(KRB5KRB_AP_ERR_MODIFIED); ptr = packet->data; /* verify length */ plen = (*ptr++ & 0xff); plen = (plen<<8) | (*ptr++ & 0xff); if (plen != packet->length) { /* * MS KDCs *may* send back a KRB_ERROR. Although * not 100% correct via RFC3244, it's something * we can workaround here. */ if (krb5_is_krb_error(packet)) { if ((ret = krb5_rd_error(context, packet, &krberror))) return(ret); if (krberror->e_data.data == NULL) { ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error; krb5_free_error(context, krberror); return (ret); } } else { return(KRB5KRB_AP_ERR_MODIFIED); } } /* verify version number */ vno = (*ptr++ & 0xff); vno = (vno<<8) | (*ptr++ & 0xff); /* * when the servers update to v2 of the protocol, * "2" will be a valid version number here */ if (vno != 1 && vno != 2) return (KRB5KDC_ERR_BAD_PVNO); /* read, check ap-rep length */ ap_rep.length = (*ptr++ & 0xff); ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff); if (ptr + ap_rep.length >= packet->data + packet->length) return(KRB5KRB_AP_ERR_MODIFIED); if (ap_rep.length) { /* verify ap_rep */ ap_rep.data = ptr; ptr += ap_rep.length; /* * Save send_subkey to later smash recv_subkey. */ ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp); if (ret) return ret; ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); if (ret) { krb5_free_keyblock(context, tmp); return(ret); } krb5_free_ap_rep_enc_part(context, ap_rep_enc); /* extract and decrypt the result */ cipherresult.data = ptr; cipherresult.length = (packet->data + packet->length) - ptr; /* * Smash recv_subkey to be send_subkey, per spec. */ ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp); krb5_free_keyblock(context, tmp); if (ret) return ret; ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, &replay); if (ret) return(ret); } else { cipherresult.data = ptr; cipherresult.length = (packet->data + packet->length) - ptr; if ((ret = krb5_rd_error(context, &cipherresult, &krberror))) return(ret); clearresult = krberror->e_data; } if (clearresult.length < 2) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } ptr = clearresult.data; local_result_code = (*ptr++ & 0xff); local_result_code = (local_result_code<<8) | (*ptr++ & 0xff); if (result_code) *result_code = local_result_code; /* * Make sure the result code is in range for this * protocol. */ if ((local_result_code < KRB5_KPASSWD_SUCCESS) || (local_result_code > KRB5_KPASSWD_ETYPE_NOSUPP)) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } /* all success replies should be authenticated/encrypted */ if ((ap_rep.length == 0) && (local_result_code == KRB5_KPASSWD_SUCCESS)) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } result_data->length = (clearresult.data + clearresult.length) - ptr; if (result_data->length) { result_data->data = (char *) malloc(result_data->length); if (result_data->data == NULL) { ret = ENOMEM; goto cleanup; } memcpy(result_data->data, ptr, result_data->length); } else { result_data->data = NULL; } ret = 0; cleanup: if (ap_rep.length) { krb5_xfree(clearresult.data); } else { krb5_free_error(context, krberror); } return(ret); } /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ #include #include #include "client_internal.h" int _kadm5_check_handle(void *handle) { CHECK_HANDLE(handle); return 0; } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved */ /* * Copyright (C) 1998 by the FundsXpress, INC. * * All rights reserved. * * Export of this software from the United States of America may require * a specific license from the United States Government. It is the * responsibility of any person or organization contemplating export to * obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. FundsXpress makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include "autoconf.h" #ifdef HAVE_MEMORY_H #include #endif #include #include #include #include #include #include /* for KRB5_ADM_DEFAULT_PORT */ #include #ifdef __STDC__ #include #endif #include #include #include #include "client_internal.h" #include #include #include #include #include #include #include "iprop.h" #define ADM_CCACHE "/tmp/ovsec_adm.XXXXXX" static int old_auth_gssapi = 0; /* connection timeout to kadmind in seconds */ #define KADMIND_CONNECT_TIMEOUT 25 int _kadm5_check_handle(); enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS }; static kadm5_ret_t _kadm5_init_any(char *client_name, enum init_type init_type, char *pass, krb5_ccache ccache_in, char *service_name, kadm5_config_params *params, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle); kadm5_ret_t kadm5_init_with_creds(char *client_name, krb5_ccache ccache, char *service_name, kadm5_config_params *params, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle) { return _kadm5_init_any(client_name, INIT_CREDS, NULL, ccache, service_name, params, struct_version, api_version, db_args, server_handle); } kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass, char *service_name, kadm5_config_params *params, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle) { return _kadm5_init_any(client_name, INIT_PASS, pass, NULL, service_name, params, struct_version, api_version, db_args, server_handle); } kadm5_ret_t kadm5_init(char *client_name, char *pass, char *service_name, kadm5_config_params *params, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle) { return _kadm5_init_any(client_name, INIT_PASS, pass, NULL, service_name, params, struct_version, api_version, db_args, server_handle); } kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab, char *service_name, kadm5_config_params *params, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle) { return _kadm5_init_any(client_name, INIT_SKEY, keytab, NULL, service_name, params, struct_version, api_version, db_args, server_handle); } krb5_error_code kadm5_free_config_params(); static void display_status_1(m, code, type, mech) char *m; OM_uint32 code; int type; const gss_OID mech; { OM_uint32 maj_stat, min_stat; gss_buffer_desc msg = GSS_C_EMPTY_BUFFER; OM_uint32 msg_ctx; msg_ctx = 0; ADMIN_LOG(LOG_ERR, "%s\n", m); /* LINTED */ while (1) { maj_stat = gss_display_status(&min_stat, code, type, mech, &msg_ctx, &msg); if (maj_stat != GSS_S_COMPLETE) { syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "error in gss_display_status" " called from <%s>\n"), m); break; } else syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "GSS-API error : %s\n"), m); syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "GSS-API error : %s\n"), (char *)msg.value); if (msg.length != 0) (void) gss_release_buffer(&min_stat, &msg); if (!msg_ctx) break; } } /* * Function: display_status * * Purpose: displays GSS-API messages * * Arguments: * * msg a string to be displayed with the message * maj_stat the GSS-API major status code * min_stat the GSS-API minor status code * mech kerberos mech * Effects: * * The GSS-API messages associated with maj_stat and min_stat are * displayed on stderr, each preceeded by "GSS-API error : " and * followed by a newline. */ void display_status(msg, maj_stat, min_stat, mech) char *msg; OM_uint32 maj_stat; OM_uint32 min_stat; char *mech; { gss_OID mech_oid; if (!rpc_gss_mech_to_oid(mech, (rpc_gss_OID *)&mech_oid)) { ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, "Invalid mechanism oid <%s>"), mech); return; } display_status_1(msg, maj_stat, GSS_C_GSS_CODE, mech_oid); display_status_1(msg, min_stat, GSS_C_MECH_CODE, mech_oid); } /* * Open an fd for the given address and connect asynchronously. Wait * KADMIND_CONNECT_TIMEOUT seconds or till it succeeds. If it succeeds * change fd to blocking and return it, else return -1. */ static int get_connection(struct netconfig *nconf, struct netbuf netaddr) { struct t_info tinfo; struct t_call sndcall; struct t_call *rcvcall = NULL; int connect_time; int flags; int fd; (void) memset(&tinfo, 0, sizeof (tinfo)); /* we'l open with O_NONBLOCK and avoid an fcntl */ fd = t_open(nconf->nc_device, O_RDWR | O_NONBLOCK, &tinfo); if (fd == -1) { return (-1); } if (t_bind(fd, (struct t_bind *)NULL, (struct t_bind *)NULL) == -1) { (void) close(fd); return (-1); } /* we can't connect unless fd is in IDLE state */ if (t_getstate(fd) != T_IDLE) { (void) close(fd); return (-1); } /* setup connect parameters */ netaddr.len = netaddr.maxlen = __rpc_get_a_size(tinfo.addr); sndcall.addr = netaddr; sndcall.opt.len = sndcall.udata.len = 0; /* we wait for KADMIND_CONNECT_TIMEOUT seconds from now */ connect_time = time(NULL) + KADMIND_CONNECT_TIMEOUT; if (t_connect(fd, &sndcall, rcvcall) != 0) { if (t_errno != TNODATA) { (void) close(fd); return (-1); } } /* loop till success or timeout */ for (;;) { if (t_rcvconnect(fd, rcvcall) == 0) break; if (t_errno != TNODATA || time(NULL) > connect_time) { /* we have either timed out or caught an error */ (void) close(fd); if (rcvcall != NULL) t_free((char *)rcvcall, T_CALL); return (-1); } sleep(1); } /* make the fd blocking (synchronous) */ flags = fcntl(fd, F_GETFL, 0); (void) fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); if (rcvcall != NULL) t_free((char *)rcvcall, T_CALL); return (fd); } /* * Open an RPCSEC_GSS connection and * get a client handle to use for future RPCSEC calls. * * This function is only used when changing passwords and * the kpasswd_protocol is RPCSEC_GSS */ static int _kadm5_initialize_rpcsec_gss_handle(kadm5_server_handle_t handle, char *client_name, char *service_name) { struct netbuf netaddr; struct hostent *hp; int fd; struct sockaddr_in addr; struct sockaddr_in *sin; struct netconfig *nconf; int code = 0; generic_ret *r; char *ccname_orig; char *iprop_svc; boolean_t iprop_enable = B_FALSE; char mech[] = "kerberos_v5"; gss_OID mech_oid; gss_OID_set_desc oid_set; gss_name_t gss_client; gss_buffer_desc input_name; gss_cred_id_t gss_client_creds = GSS_C_NO_CREDENTIAL; rpc_gss_options_req_t options_req; rpc_gss_options_ret_t options_ret; rpc_gss_service_t service = rpc_gss_svc_privacy; OM_uint32 gssstat, minor_stat; void *handlep; enum clnt_stat rpc_err_code; char *server = handle->params.admin_server; /* * Try to find the kpasswd_server first if this is for the changepw * service. If defined then it should be resolvable else return error. */ if (strncmp(service_name, KADM5_CHANGEPW_HOST_SERVICE, strlen(KADM5_CHANGEPW_HOST_SERVICE)) == 0) { if (handle->params.kpasswd_server != NULL) server = handle->params.kpasswd_server; } hp = gethostbyname(server); if (hp == (struct hostent *)NULL) { code = KADM5_BAD_SERVER_NAME; ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "bad server name\n")); goto cleanup; } 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((ushort_t)handle->params.kadmind_port); sin = &addr; #ifdef DEBUG printf("kadmin_port %d\n", handle->params.kadmind_port); printf("addr: sin_port: %d, sin_family: %d, sin_zero %s\n", addr.sin_port, addr.sin_family, addr.sin_zero); printf("sin_addr %d:%d\n", addr.sin_addr.S_un.S_un_w.s_w1, addr.sin_addr.S_un.S_un_w.s_w2); #endif if ((handlep = setnetconfig()) == (void *) NULL) { (void) syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "cannot get any transport information")); goto error; } while (nconf = getnetconfig(handlep)) { if ((nconf->nc_semantics == NC_TPI_COTS_ORD) && (strcmp(nconf->nc_protofmly, NC_INET) == 0) && (strcmp(nconf->nc_proto, NC_TCP) == 0)) break; } if (nconf == (struct netconfig *)NULL) goto error; /* Transform addr to netbuf */ (void) memset(&netaddr, 0, sizeof (netaddr)); netaddr.buf = (char *)sin; /* get an fd connected to the given address */ fd = get_connection(nconf, netaddr); if (fd == -1) { syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "unable to open connection to ADMIN server " "(t_error %i)"), t_errno); code = KADM5_RPC_ERROR; goto error; } #ifdef DEBUG printf("fd: %d, KADM: %d, KADMVERS %d\n", fd, KADM, KADMVERS); printf("nconf: nc_netid: %s, nc_semantics: %d, nc_flag: %d, " "nc_protofmly: %s\n", nconf->nc_netid, nconf->nc_semantics, nconf->nc_flag, nconf->nc_protofmly); printf("nc_proto: %s, nc_device: %s, nc_nlookups: %d, nc_used: %d\n", nconf->nc_proto, nconf->nc_device, nconf->nc_nlookups, nconf->nc_unused); printf("netaddr: maxlen %d, buf: %s, len: %d\n", netaddr.maxlen, netaddr.buf, netaddr.len); #endif /* * Tell clnt_tli_create that given fd is already connected * * If the service_name and client_name are iprop-centric, * we need to clnt_tli_create to the appropriate RPC prog */ iprop_svc = strdup(KIPROP_SVC_NAME); if (iprop_svc == NULL) return (ENOMEM); if ((strstr(service_name, iprop_svc) != NULL) && (strstr(client_name, iprop_svc) != NULL)) { iprop_enable = B_TRUE; handle->clnt = clnt_tli_create(fd, nconf, NULL, KRB5_IPROP_PROG, KRB5_IPROP_VERS, 0, 0); } else handle->clnt = clnt_tli_create(fd, nconf, NULL, KADM, KADMVERS, 0, 0); if (iprop_svc) free(iprop_svc); if (handle->clnt == NULL) { syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "clnt_tli_create failed\n")); code = KADM5_RPC_ERROR; (void) close(fd); goto error; } /* * The rpc-handle was created on an fd opened and connected * by us, so we have to explicitly tell rpc to close it. */ if (clnt_control(handle->clnt, CLSET_FD_CLOSE, NULL) != TRUE) { clnt_pcreateerror("ERROR:"); syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "clnt_control failed to set CLSET_FD_CLOSE")); code = KADM5_RPC_ERROR; (void) close(fd); goto error; } handle->lhandle->clnt = handle->clnt; /* now that handle->clnt is set, we can check the handle */ if (code = _kadm5_check_handle((void *) handle)) goto error; /* * The RPC connection is open; establish the GSS-API * authentication context. */ ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "have an rpc connection open\n")); /* use the kadm5 cache */ ccname_orig = getenv("KRB5CCNAME"); if (ccname_orig) ccname_orig = strdup(ccname_orig); (void) krb5_setenv("KRB5CCNAME", handle->cache_name, 1); ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, "current credential cache: %s"), handle->cache_name); input_name.value = client_name; input_name.length = strlen((char *)input_name.value) + 1; gssstat = gss_import_name(&minor_stat, &input_name, (gss_OID)gss_nt_krb5_name, &gss_client); if (gssstat != GSS_S_COMPLETE) { code = KADM5_GSS_ERROR; ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "gss_import_name failed for client name\n")); goto error; } if (!rpc_gss_mech_to_oid(mech, (rpc_gss_OID *)&mech_oid)) { ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, "Invalid mechanism oid <%s>"), mech); goto error; } oid_set.count = 1; oid_set.elements = mech_oid; gssstat = gss_acquire_cred(&minor_stat, gss_client, 0, &oid_set, GSS_C_INITIATE, &gss_client_creds, NULL, NULL); (void) gss_release_name(&minor_stat, &gss_client); if (gssstat != GSS_S_COMPLETE) { code = KADM5_GSS_ERROR; ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, "could not acquire credentials, " "major error code: %d\n"), gssstat); goto error; } handle->my_cred = gss_client_creds; options_req.my_cred = gss_client_creds; options_req.req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG; options_req.time_req = 0; options_req.input_channel_bindings = NULL; #ifndef INIT_TEST handle->clnt->cl_auth = rpc_gss_seccreate(handle->clnt, service_name, mech, service, NULL, &options_req, &options_ret); #endif /* ! INIT_TEST */ if (ccname_orig) { (void) krb5_setenv("KRB5CCNAME", ccname_orig, 1); free(ccname_orig); } else (void) krb5_unsetenv("KRB5CCNAME"); if (handle->clnt->cl_auth == NULL) { code = KADM5_GSS_ERROR; display_status(dgettext(TEXT_DOMAIN, "rpc_gss_seccreate failed\n"), options_ret.major_status, options_ret.minor_status, mech); goto error; } /* * Bypass the remainder of the code and return straightaway * if the gss service requested is kiprop */ if (iprop_enable == B_TRUE) { code = 0; goto cleanup; } r = init_2(&handle->api_version, handle->clnt); /* Solaris Kerberos: 163 resync */ if (r == NULL) { ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "error during admin api initialization\n")); code = KADM5_RPC_ERROR; goto error; } if (r->code) { code = r->code; ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, "error during admin api initialization: %d\n"), r->code); goto error; } error: cleanup: if (handlep != (void *) NULL) (void) endnetconfig(handlep); /* * gss_client_creds is freed only when there is an error condition, * given that rpc_gss_seccreate() will assign the cred pointer to the * my_cred member in the auth handle's private data structure. */ if (code && (gss_client_creds != GSS_C_NO_CREDENTIAL)) (void) gss_release_cred(&minor_stat, &gss_client_creds); return (code); } static kadm5_ret_t _kadm5_init_any(char *client_name, enum init_type init_type, char *pass, krb5_ccache ccache_in, char *service_name, kadm5_config_params *params_in, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle) { int i; krb5_creds creds; krb5_ccache ccache = NULL; krb5_timestamp now; OM_uint32 gssstat, minor_stat; kadm5_server_handle_t handle; kadm5_config_params params_local; int code = 0; krb5_get_init_creds_opt opt; gss_buffer_desc input_name; krb5_error_code kret; krb5_int32 starttime; char *server = NULL; krb5_principal serverp = NULL, clientp = NULL; krb5_principal saved_server = NULL; bool_t cpw = FALSE; ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "entering kadm5_init_any\n")); if (! server_handle) { return EINVAL; } if (! (handle = malloc(sizeof(*handle)))) { return ENOMEM; } if (! (handle->lhandle = malloc(sizeof(*handle)))) { free(handle); return ENOMEM; } handle->magic_number = KADM5_SERVER_HANDLE_MAGIC; handle->struct_version = struct_version; handle->api_version = api_version; handle->clnt = 0; handle->cache_name = 0; handle->destroy_cache = 0; *handle->lhandle = *handle; handle->lhandle->api_version = KADM5_API_VERSION_2; handle->lhandle->struct_version = KADM5_STRUCT_VERSION; handle->lhandle->lhandle = handle->lhandle; kret = krb5_init_context(&handle->context); if (kret) { free(handle->lhandle); free(handle); return (kret); } if(service_name == NULL || client_name == NULL) { krb5_free_context(handle->context); free(handle->lhandle); free(handle); return EINVAL; } memset((char *) &creds, 0, sizeof(creds)); /* * Verify the version numbers before proceeding; we can't use * CHECK_HANDLE because not all fields are set yet. */ GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION, KADM5_NEW_LIB_API_VERSION); /* * Acquire relevant profile entries. In version 2, merge values * in params_in with values from profile, based on * params_in->mask. * * In version 1, we've given a realm (which may be NULL) instead * of params_in. So use that realm, make params_in contain an * empty mask, and behave like version 2. */ memset((char *) ¶ms_local, 0, sizeof(params_local)); if (api_version == KADM5_API_VERSION_1) { if (params_in) params_local.mask = KADM5_CONFIG_REALM; params_in = ¶ms_local; } #define ILLEGAL_PARAMS ( \ KADM5_CONFIG_ACL_FILE | KADM5_CONFIG_ADB_LOCKFILE | \ KADM5_CONFIG_DBNAME | KADM5_CONFIG_ADBNAME | \ KADM5_CONFIG_DICT_FILE | KADM5_CONFIG_ADMIN_KEYTAB | \ KADM5_CONFIG_STASH_FILE | KADM5_CONFIG_MKEY_NAME | \ KADM5_CONFIG_ENCTYPE | KADM5_CONFIG_MAX_LIFE | \ KADM5_CONFIG_MAX_RLIFE | KADM5_CONFIG_EXPIRATION | \ KADM5_CONFIG_FLAGS | KADM5_CONFIG_ENCTYPES | \ KADM5_CONFIG_MKEY_FROM_KBD) if (params_in && params_in->mask & ILLEGAL_PARAMS) { krb5_free_context(handle->context); free(handle->lhandle); free(handle); ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, "bad client parameters, returning %d"), KADM5_BAD_CLIENT_PARAMS); return KADM5_BAD_CLIENT_PARAMS; } if ((code = kadm5_get_config_params(handle->context, 0, params_in, &handle->params))) { krb5_free_context(handle->context); free(handle->lhandle); free(handle); ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, "failed to get config_params, return: %d\n"), code); return(code); } #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \ KADM5_CONFIG_ADMIN_SERVER | \ KADM5_CONFIG_KADMIND_PORT) #define KPW_REQUIRED_PARAMS (KADM5_CONFIG_REALM | \ KADM5_CONFIG_KPASSWD_SERVER | \ KADM5_CONFIG_KPASSWD_PORT) if (((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) && ((handle->params.mask & KPW_REQUIRED_PARAMS) != KPW_REQUIRED_PARAMS)) { (void) kadm5_free_config_params(handle->context, &handle->params); krb5_free_context(handle->context); free(handle->lhandle); free(handle); ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "missing config parameters\n")); return KADM5_MISSING_KRB5_CONF_PARAMS; } /* * Acquire a service ticket for service_name@realm in the name of * client_name, using password pass (which could be NULL), and * create a ccache to store them in. If INIT_CREDS, use the * ccache we were provided instead. */ if ((code = krb5_parse_name(handle->context, client_name, &creds.client))) { ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "could not parse client name\n")); goto error; } clientp = creds.client; if (strncmp(service_name, KADM5_CHANGEPW_HOST_SERVICE, strlen(KADM5_CHANGEPW_HOST_SERVICE)) == 0) cpw = TRUE; if (init_type == INIT_PASS && handle->params.kpasswd_protocol == KRB5_CHGPWD_CHANGEPW_V2 && cpw == TRUE) { /* * The 'service_name' is constructed by the caller * but its done before the parameter which determines * the kpasswd_protocol is found. The servers that * support the SET/CHANGE password protocol expect * a slightly different service principal than * the normal SEAM kadmind so construct the correct * name here and then forget it. */ char *newsvcname = NULL; newsvcname = malloc(strlen(KADM5_CHANGEPW_SERVICE) + strlen(handle->params.realm) + 2); if (newsvcname == NULL) { ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "could not malloc\n")); code = ENOMEM; goto error; } sprintf(newsvcname, "%s@%s", KADM5_CHANGEPW_SERVICE, handle->params.realm); if ((code = krb5_parse_name(handle->context, newsvcname, &creds.server))) { ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "could not parse server " "name\n")); free(newsvcname); goto error; } free(newsvcname); } else { input_name.value = service_name; input_name.length = strlen((char *)input_name.value) + 1; gssstat = krb5_gss_import_name(&minor_stat, &input_name, (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, (gss_name_t *)&creds.server); if (gssstat != GSS_S_COMPLETE) { code = KADM5_GSS_ERROR; ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "gss_import_name failed for client name\n")); goto error; } } serverp = creds.server; /* XXX temporarily fix a bug in krb5_cc_get_type */ #undef krb5_cc_get_type #define krb5_cc_get_type(context, cache) ((cache)->ops->prefix) if (init_type == INIT_CREDS) { ccache = ccache_in; handle->cache_name = (char *) malloc(strlen(krb5_cc_get_type(handle->context, ccache)) + strlen(krb5_cc_get_name(handle->context, ccache)) + 2); if (handle->cache_name == NULL) { code = ENOMEM; goto error; } sprintf(handle->cache_name, "%s:%s", krb5_cc_get_type(handle->context, ccache), krb5_cc_get_name(handle->context, ccache)); } else { #if 0 handle->cache_name = (char *) malloc(strlen(ADM_CCACHE)+strlen("FILE:")+1); if (handle->cache_name == NULL) { code = ENOMEM; goto error; } sprintf(handle->cache_name, "FILE:%s", ADM_CCACHE); mktemp(handle->cache_name + strlen("FILE:")); #endif { static int counter = 0; handle->cache_name = malloc(sizeof("MEMORY:kadm5_") + 3*sizeof(counter)); sprintf(handle->cache_name, "MEMORY:kadm5_%u", counter++); } if ((code = krb5_cc_resolve(handle->context, handle->cache_name, &ccache))) goto error; if ((code = krb5_cc_initialize (handle->context, ccache, creds.client))) goto error; handle->destroy_cache = 1; } handle->lhandle->cache_name = handle->cache_name; ADMIN_LOG(LOG_ERR, dgettext(TEXT_DOMAIN, "cache created: %s\n"), handle->cache_name); if ((code = krb5_timeofday(handle->context, &now))) goto error; /* * Get a ticket, use the method specified in init_type. */ creds.times.starttime = 0; /* start timer at KDC */ creds.times.endtime = 0; /* endtime will be limited by service */ memset(&opt, 0, sizeof (opt)); krb5_get_init_creds_opt_init(&opt); if (creds.times.endtime) { if (creds.times.starttime) starttime = creds.times.starttime; else starttime = now; krb5_get_init_creds_opt_set_tkt_life(&opt, creds.times.endtime - starttime); } code = krb5_unparse_name(handle->context, creds.server, &server); if (code) goto error; /* * Solaris Kerberos: * Save the original creds.server as krb5_get_init_creds*() always * sets the realm of the server to the client realm. */ code = krb5_copy_principal(handle->context, creds.server, &saved_server); if (code) goto error; if (init_type == INIT_PASS) { code = krb5_get_init_creds_password(handle->context, &creds, creds.client, pass, NULL, NULL, creds.times.starttime, server, &opt); } else if (init_type == INIT_SKEY) { krb5_keytab kt = NULL; if (!(pass && (code = krb5_kt_resolve(handle->context, pass, &kt)))) { code = krb5_get_init_creds_keytab( handle->context, &creds, creds.client, kt, creds.times.starttime, server, &opt); if (pass) krb5_kt_close(handle->context, kt); } } /* Improved error messages */ if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) code = KADM5_BAD_PASSWORD; if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) code = KADM5_SECURE_PRINC_MISSING; if (code != 0) { ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "failed to obtain credentials cache\n")); krb5_free_principal(handle->context, saved_server); goto error; } /* * Solaris Kerberos: * If the server principal had an empty realm then store that in * the cred cache and not the server realm as returned by * krb5_get_init_creds_{keytab|password}(). This ensures that rpcsec_gss * will find the credential in the cred cache even if a "fallback" * method is being used to determine the realm. */ if (init_type != INIT_CREDS) { krb5_free_principal(handle->context, creds.server); } creds.server = saved_server; /* * If we got this far, save the creds in the cache. */ if (ccache) { code = krb5_cc_store_cred(handle->context, ccache, &creds); } ADMIN_LOGO(LOG_ERR, dgettext(TEXT_DOMAIN, "obtained credentials cache\n")); #ifdef ZEROPASSWD if (pass != NULL) memset(pass, 0, strlen(pass)); #endif if (init_type != INIT_PASS || handle->params.kpasswd_protocol == KRB5_CHGPWD_RPCSEC || cpw == FALSE) { code = _kadm5_initialize_rpcsec_gss_handle(handle, client_name, service_name); /* * Solaris Kerberos: * If _kadm5_initialize_rpcsec_gss_handle() fails it will have * called krb5_gss_release_cred(). If the credential cache is a * MEMORY cred cache krb5_gss_release_cred() destroys the * cred cache data. Make sure that the cred-cache is closed * to prevent a double free in the "error" code. */ if (code != 0) { if (init_type != INIT_CREDS) { krb5_cc_close(handle->context, ccache); ccache = NULL; } goto error; } } *server_handle = (void *) handle; if (init_type != INIT_CREDS) krb5_cc_close(handle->context, ccache); goto cleanup; error: /* * Note that it is illegal for this code to execute if "handle" * has not been allocated and initialized. I.e., don't use "goto * error" before the block of code at the top of the function * that allocates and initializes "handle". */ if (handle->cache_name) free(handle->cache_name); if (handle->destroy_cache && ccache) krb5_cc_destroy(handle->context, ccache); if(handle->clnt && handle->clnt->cl_auth) AUTH_DESTROY(handle->clnt->cl_auth); if(handle->clnt) clnt_destroy(handle->clnt); (void) kadm5_free_config_params(handle->context, &handle->params); cleanup: if (server) free(server); /* * cred's server and client pointers could have been overwritten * by the krb5_get_init_* functions. If the addresses are different * before and after the calls then we must free the memory that * was allocated before the call. */ if (clientp && clientp != creds.client) krb5_free_principal(handle->context, clientp); if (serverp && serverp != creds.server) krb5_free_principal(handle->context, serverp); krb5_free_cred_contents(handle->context, &creds); /* * Dont clean up the handle if the code is OK (code==0) * because it is returned to the caller in the 'server_handle' * ptr. */ if (code) { krb5_free_context(handle->context); free(handle->lhandle); free(handle); } return code; } kadm5_ret_t kadm5_destroy(void *server_handle) { krb5_ccache ccache = NULL; int code = KADM5_OK; kadm5_server_handle_t handle = (kadm5_server_handle_t) server_handle; OM_uint32 min_stat; CHECK_HANDLE(server_handle); /* SUNW14resync: * krb5_cc_resolve() will resolve a ccache with the same data that * handle->my_cred points to. If the ccache is a MEMORY ccache then * gss_release_cred() will free that data (it doesn't do this when ccache * is a FILE ccache). * if'ed out to avoid the double free. */ #if 0 if (handle->destroy_cache && handle->cache_name) { if ((code = krb5_cc_resolve(handle->context, handle->cache_name, &ccache)) == 0) code = krb5_cc_destroy (handle->context, ccache); } #endif if (handle->cache_name) free(handle->cache_name); if (handle->clnt && handle->clnt->cl_auth) { /* * Since kadm5 doesn't use the default credentials we * must clean this up manually. */ if (handle->my_cred != GSS_C_NO_CREDENTIAL) (void) gss_release_cred(&min_stat, &handle->my_cred); AUTH_DESTROY(handle->clnt->cl_auth); } if (handle->clnt) clnt_destroy(handle->clnt); if (handle->lhandle) free (handle->lhandle); kadm5_free_config_params(handle->context, &handle->params); krb5_free_context(handle->context); handle->magic_number = 0; free(handle); return code; } /* not supported on client */ kadm5_ret_t kadm5_lock(void *server_handle) { return EINVAL; } /* not supported on client */ kadm5_ret_t kadm5_unlock(void *server_handle) { return EINVAL; } kadm5_ret_t kadm5_flush(void *server_handle) { return KADM5_OK; } int _kadm5_check_handle(void *handle) { CHECK_HANDLE(handle); return 0; } 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); } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef __KADM5_CLIENT_INTERNAL_H__ #define __KADM5_CLIENT_INTERNAL_H__ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ * * $Log$ * Revision 1.1 1996/07/24 22:22:43 tlyu * * Makefile.in, configure.in: break out client lib into a * subdirectory * * Revision 1.11 1996/07/22 20:35:46 marc * this commit includes all the changes on the OV_9510_INTEGRATION and * OV_MERGE branches. This includes, but is not limited to, the new openvision * admin system, and major changes to gssapi to add functionality, and bring * the implementation in line with rfc1964. before committing, the * code was built and tested for netbsd and solaris. * * Revision 1.10.4.1 1996/07/18 03:08:37 marc * merged in changes from OV_9510_BP to OV_9510_FINAL1 * * Revision 1.10.2.1 1996/06/20 02:16:46 marc * File added to the repository on a branch * * Revision 1.10 1996/06/06 20:09:16 bjaspan * add destroy_cache, for kadm5_init_with_creds * * Revision 1.9 1996/05/30 21:04:42 bjaspan * add lhandle to handle * * Revision 1.8 1996/05/28 20:33:49 bjaspan * rework kadm5_config * * Revision 1.7 1996/05/17 21:36:59 bjaspan * rename to kadm5, begin implementing version 2 * * Revision 1.6 1996/05/16 21:45:07 bjaspan * add context * * Revision 1.5 1996/05/08 21:10:23 bjaspan * marc's changes * * Revision 1.4 1996/01/16 20:54:30 grier * secure/3570 use krb5_ui_4 not unsigned int * * Revision 1.3 1995/11/14 17:48:57 grier * long to int * * Revision 1.2 1994/08/16 18:53:47 jik * Versioning stuff. * * Revision 1.1 1994/08/09 21:14:38 jik * Initial revision * */ /* * This header file is used internally by the Admin API client * libraries. IF YOU THINK YOU NEED TO USE THIS FILE FOR ANYTHING, * YOU'RE ALMOST CERTAINLY WRONG. */ #ifdef __cplusplus extern "C" { #endif #include "admin_internal.h" typedef struct _kadm5_server_handle_t { krb5_ui_4 magic_number; krb5_ui_4 struct_version; krb5_ui_4 api_version; char * cache_name; int destroy_cache; CLIENT * clnt; krb5_context context; /* Solaris Kerberos */ gss_cred_id_t my_cred; kadm5_config_params params; struct _kadm5_server_handle_t *lhandle; } kadm5_server_handle_rec, *kadm5_server_handle_t; #define CLIENT_CHECK_HANDLE(handle) \ { \ kadm5_server_handle_t srvr = \ (kadm5_server_handle_t) handle; \ \ if (srvr->params.kpasswd_protocol == KRB5_CHGPWD_RPCSEC && ! srvr->clnt) \ return KADM5_BAD_SERVER_HANDLE; \ if (! srvr->cache_name) \ return KADM5_BAD_SERVER_HANDLE; \ if (! srvr->lhandle) \ return KADM5_BAD_SERVER_HANDLE; \ } #define CHECK_HANDLE(handle) \ GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION, \ KADM5_NEW_LIB_API_VERSION) \ CLIENT_CHECK_HANDLE(handle) #ifdef __cplusplus } #endif #endif /* __KADM5_CLIENT_INTERNAL_H__ */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; #endif #include /* SUNWresync121 XXX */ #include #include #ifdef HAVE_MEMORY_H #include #endif #include #include "client_internal.h" #ifdef DEBUG /* SUNWresync14 XXX */ #define eret() {clnt_perror(handle->clnt, "null ret"); return KADM5_RPC_ERROR;} #else #define eret() return KADM5_RPC_ERROR #endif kadm5_ret_t kadm5_create_principal(void *server_handle, kadm5_principal_ent_t princ, long mask, char *pw) { generic_ret *r; cprinc_arg arg; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); memset(&arg, 0, sizeof(arg)); arg.mask = mask; arg.passwd = pw; arg.api_version = handle->api_version; if(princ == NULL) return EINVAL; if (handle->api_version == KADM5_API_VERSION_1) { memcpy(&arg.rec, princ, sizeof(kadm5_principal_ent_rec_v1)); } else { memcpy(&arg.rec, princ, sizeof(kadm5_principal_ent_rec)); } if (handle->api_version == KADM5_API_VERSION_1) { /* * hack hack cough cough. * krb5_unparse name dumps core if we pass it in garbage * or null. So, since the client is not allowed to set mod_name * anyway, we just fill it in with a dummy principal. The server of * course ignores this. */ /* krb5_parse_name(handle->context, "bogus/bogus", &arg.rec.mod_name); */ arg.rec.mod_name = NULL; } else arg.rec.mod_name = NULL; if(!(mask & KADM5_POLICY)) arg.rec.policy = NULL; if (! (mask & KADM5_KEY_DATA)) { arg.rec.n_key_data = 0; arg.rec.key_data = NULL; } if (! (mask & KADM5_TL_DATA)) { arg.rec.n_tl_data = 0; arg.rec.tl_data = NULL; } r = create_principal_2(&arg, handle->clnt); if (handle->api_version == KADM5_API_VERSION_1) krb5_free_principal(handle->context, arg.rec.mod_name); if(r == NULL) eret(); return r->code; } kadm5_ret_t kadm5_create_principal_3(void *server_handle, kadm5_principal_ent_t princ, long mask, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char *pw) { generic_ret *r; cprinc3_arg arg; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); memset(&arg, 0, sizeof(arg)); arg.mask = mask; arg.passwd = pw; arg.api_version = handle->api_version; arg.n_ks_tuple = n_ks_tuple; arg.ks_tuple = ks_tuple; if(princ == NULL) return EINVAL; if (handle->api_version == KADM5_API_VERSION_1) { memcpy(&arg.rec, princ, sizeof(kadm5_principal_ent_rec_v1)); } else { memcpy(&arg.rec, princ, sizeof(kadm5_principal_ent_rec)); } if (handle->api_version == KADM5_API_VERSION_1) { /* * hack hack cough cough. * krb5_unparse name dumps core if we pass it in garbage * or null. So, since the client is not allowed to set mod_name * anyway, we just fill it in with a dummy principal. The server of * course ignores this. */ krb5_parse_name(handle->context, "bogus/bogus", &arg.rec.mod_name); } else arg.rec.mod_name = NULL; if(!(mask & KADM5_POLICY)) arg.rec.policy = NULL; if (! (mask & KADM5_KEY_DATA)) { arg.rec.n_key_data = 0; arg.rec.key_data = NULL; } if (! (mask & KADM5_TL_DATA)) { arg.rec.n_tl_data = 0; arg.rec.tl_data = NULL; } r = create_principal3_2(&arg, handle->clnt); if (handle->api_version == KADM5_API_VERSION_1) krb5_free_principal(handle->context, arg.rec.mod_name); if(r == NULL) eret(); return r->code; } kadm5_ret_t kadm5_delete_principal(void *server_handle, krb5_principal principal) { dprinc_arg arg; generic_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); if(principal == NULL) return EINVAL; arg.princ = principal; arg.api_version = handle->api_version; r = delete_principal_2(&arg, handle->clnt); if(r == NULL) eret(); return r->code; } kadm5_ret_t kadm5_modify_principal(void *server_handle, kadm5_principal_ent_t princ, long mask) { mprinc_arg arg; generic_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); memset(&arg, 0, sizeof(arg)); arg.mask = mask; arg.api_version = handle->api_version; /* * cough cough gag gag * see comment in create_principal. */ if(princ == NULL) return EINVAL; if (handle->api_version == KADM5_API_VERSION_1) { memcpy(&arg.rec, princ, sizeof(kadm5_principal_ent_rec_v1)); } else { memcpy(&arg.rec, princ, sizeof(kadm5_principal_ent_rec)); } if(!(mask & KADM5_POLICY)) arg.rec.policy = NULL; if (! (mask & KADM5_KEY_DATA)) { arg.rec.n_key_data = 0; arg.rec.key_data = NULL; } if (! (mask & KADM5_TL_DATA)) { arg.rec.n_tl_data = 0; arg.rec.tl_data = NULL; } if (handle->api_version == KADM5_API_VERSION_1) { /* * See comment in create_principal */ krb5_parse_name(handle->context, "bogus/bogus", &arg.rec.mod_name); } else arg.rec.mod_name = NULL; r = modify_principal_2(&arg, handle->clnt); if (handle->api_version == KADM5_API_VERSION_1) krb5_free_principal(handle->context, arg.rec.mod_name); if(r == NULL) eret(); return r->code; } kadm5_ret_t kadm5_get_principal(void *server_handle, krb5_principal princ, kadm5_principal_ent_t ent, long mask) { gprinc_arg arg; gprinc_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); if(princ == NULL) return EINVAL; arg.princ = princ; if (handle->api_version == KADM5_API_VERSION_1) arg.mask = KADM5_PRINCIPAL_NORMAL_MASK; else arg.mask = mask; arg.api_version = handle->api_version; r = get_principal_2(&arg, handle->clnt); if(r == NULL) eret(); if (handle->api_version == KADM5_API_VERSION_1) { kadm5_principal_ent_t_v1 *entp; entp = (kadm5_principal_ent_t_v1 *) ent; if (r->code == 0) { if (!(*entp = (kadm5_principal_ent_t_v1) malloc(sizeof(kadm5_principal_ent_rec_v1)))) return ENOMEM; /* this memcpy works because the v1 structure is an initial subset of the v2 struct. C guarantees that this will result in the same layout in memory */ memcpy(*entp, &r->rec, sizeof(**entp)); } else { *entp = NULL; } } else { if (r->code == 0) memcpy(ent, &r->rec, sizeof(r->rec)); } return r->code; } kadm5_ret_t kadm5_get_principals(void *server_handle, char *exp, char ***princs, int *count) { gprincs_arg arg; gprincs_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); if(princs == NULL || count == NULL) return EINVAL; arg.exp = exp; arg.api_version = handle->api_version; r = get_princs_2(&arg, handle->clnt); if(r == NULL) eret(); if(r->code == 0) { *count = r->count; *princs = r->princs; } else { *count = 0; *princs = NULL; } return r->code; } kadm5_ret_t kadm5_rename_principal(void *server_handle, krb5_principal source, krb5_principal dest) { rprinc_arg arg; generic_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); arg.src = source; arg.dest = dest; arg.api_version = handle->api_version; if (source == NULL || dest == NULL) return EINVAL; r = rename_principal_2(&arg, handle->clnt); if(r == NULL) eret(); return r->code; } kadm5_ret_t kadm5_chpass_principal(void *server_handle, krb5_principal princ, char *password) { chpass_arg arg; generic_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); arg.princ = princ; arg.pass = password; arg.api_version = handle->api_version; if(princ == NULL) return EINVAL; r = chpass_principal_2(&arg, handle->clnt); if(r == NULL) eret(); return r->code; } kadm5_ret_t kadm5_chpass_principal_3(void *server_handle, krb5_principal princ, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char *password) { chpass3_arg arg; generic_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); arg.princ = princ; arg.pass = password; arg.api_version = handle->api_version; arg.keepold = keepold; arg.n_ks_tuple = n_ks_tuple; arg.ks_tuple = ks_tuple; if(princ == NULL) return EINVAL; r = chpass_principal3_2(&arg, handle->clnt); if(r == NULL) eret(); return r->code; } kadm5_ret_t kadm5_setv4key_principal(void *server_handle, krb5_principal princ, krb5_keyblock *keyblock) { setv4key_arg arg; generic_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); arg.princ = princ; arg.keyblock = keyblock; arg.api_version = handle->api_version; if(princ == NULL || keyblock == NULL) return EINVAL; r = setv4key_principal_2(&arg, handle->clnt); if(r == NULL) eret(); return r->code; } kadm5_ret_t kadm5_setkey_principal(void *server_handle, krb5_principal princ, krb5_keyblock *keyblocks, int n_keys) { setkey_arg arg; generic_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); arg.princ = princ; arg.keyblocks = keyblocks; arg.n_keys = n_keys; arg.api_version = handle->api_version; if(princ == NULL || keyblocks == NULL) return EINVAL; r = setkey_principal_2(&arg, handle->clnt); if(r == NULL) eret(); return r->code; } kadm5_ret_t kadm5_setkey_principal_3(void *server_handle, krb5_principal princ, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, krb5_keyblock *keyblocks, int n_keys) { setkey3_arg arg; generic_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); arg.princ = princ; arg.keyblocks = keyblocks; arg.n_keys = n_keys; arg.api_version = handle->api_version; arg.keepold = keepold; arg.n_ks_tuple = n_ks_tuple; arg.ks_tuple = ks_tuple; if(princ == NULL || keyblocks == NULL) return EINVAL; r = setkey_principal3_2(&arg, handle->clnt); if(r == NULL) eret(); return r->code; } /* * Solaris Kerberos: * This routine implements just the "old" randkey_principal code. * The code in the kadmin client sometimes needs to call this * directly when the kadm5_randkey_principal_3 call fails. * * The kadmin client utility uses a specific set of key/salt tuples, * so the standard fallback in kadm5_randkey_principal (see below) * will not work because it would result in kadm5_randkey_principal_3 * being called twice - once with the specific key/salts specified by * kadmin and once with the NULL set (used to indicate that the server * should use the full set of supported enctypes). Making this * routine separate makes the code simpler and avoids making the * kadm5_randkey_principal_3 twice from kadmin. */ kadm5_ret_t kadm5_randkey_principal_old(void *server_handle, krb5_principal princ, krb5_keyblock **key, int *n_keys) { chrand_arg arg; chrand_ret *r; kadm5_server_handle_t handle = server_handle; int i, ret; /* For safety */ if (n_keys) *n_keys = 0; if (key) *key = NULL; CHECK_HANDLE(server_handle); arg.princ = princ; arg.api_version = handle->api_version; if(princ == NULL) return EINVAL; r = chrand_principal_2(&arg, handle->clnt); if (r == NULL) return KADM5_RPC_ERROR; if (handle->api_version == KADM5_API_VERSION_1) { if (key) krb5_copy_keyblock(handle->context, &r->key, key); } else if (key && (r->n_keys > 0)) { *key = (krb5_keyblock *) malloc( r->n_keys*sizeof(krb5_keyblock)); if (*key == NULL) return ENOMEM; for (i = 0; i < r->n_keys; i++) { ret = krb5_copy_keyblock_contents( handle->context, &r->keys[i], &(*key)[i]); if (ret) { free(*key); *key = NULL; return ENOMEM; } } if (n_keys) *n_keys = r->n_keys; } return (r->code); } kadm5_ret_t kadm5_randkey_principal_3(void *server_handle, krb5_principal princ, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, krb5_keyblock **key, int *n_keys) { chrand3_arg arg; chrand_ret *r; kadm5_server_handle_t handle = server_handle; int i, ret; /* Solaris Kerberos - For safety */ if (n_keys) *n_keys = 0; if (key) *key = NULL; CHECK_HANDLE(server_handle); arg.princ = princ; arg.api_version = handle->api_version; arg.keepold = keepold; arg.n_ks_tuple = n_ks_tuple; arg.ks_tuple = ks_tuple; if(princ == NULL) return EINVAL; r = chrand_principal3_2(&arg, handle->clnt); if(r == NULL) eret(); if (handle->api_version == KADM5_API_VERSION_1) { if (key) krb5_copy_keyblock(handle->context, &r->key, key); } else { if (n_keys) *n_keys = r->n_keys; if (key) { if(r->n_keys) { *key = (krb5_keyblock *) malloc(r->n_keys*sizeof(krb5_keyblock)); if (*key == NULL) return ENOMEM; for (i = 0; i < r->n_keys; i++) { ret = krb5_copy_keyblock_contents(handle->context, &r->keys[i], &(*key)[i]); if (ret) { free(*key); return ENOMEM; } } } else *key = NULL; } } return r->code; } kadm5_ret_t kadm5_randkey_principal(void *server_handle, krb5_principal princ, krb5_keyblock **key, int *n_keys) { /* Solaris Kerberos */ kadm5_ret_t kret; /* * Default to trying the newest API to insure that the full * set of enctypes is created. */ kret = kadm5_randkey_principal_3(server_handle, princ, FALSE, 0, NULL, key, n_keys); /* * We will get an RPC error if the RPC call failed which * will normally indicate that the remote procedure did not * exist on the server, so try the older API. */ if (kret == KADM5_RPC_ERROR) { kret = kadm5_randkey_principal_old(server_handle, princ, key, n_keys); } return (kret); } /* not supported on client side */ kadm5_ret_t kadm5_decrypt_key(void *server_handle, kadm5_principal_ent_t entry, krb5_int32 ktype, krb5_int32 stype, krb5_int32 kvno, krb5_keyblock *keyblock, krb5_keysalt *keysalt, int *kvnop) { return EINVAL; } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ #include /* SUNWresync121 XXX */ #include #include #include #ifdef HAVE_MEMORY_H #include #endif /* Default timeout can be changed using clnt_control() */ static struct timeval TIMEOUT = { 25, 0 }; generic_ret * create_principal_2(cprinc_arg *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, CREATE_PRINCIPAL, (xdrproc_t) xdr_cprinc_arg, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } generic_ret * create_principal3_2(cprinc3_arg *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, CREATE_PRINCIPAL3, (xdrproc_t) xdr_cprinc3_arg, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } generic_ret * delete_principal_2(dprinc_arg *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, DELETE_PRINCIPAL, (xdrproc_t) xdr_dprinc_arg, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } generic_ret * modify_principal_2(mprinc_arg *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, MODIFY_PRINCIPAL, (xdrproc_t) xdr_mprinc_arg, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } generic_ret * rename_principal_2(rprinc_arg *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, RENAME_PRINCIPAL, (xdrproc_t) xdr_rprinc_arg, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } gprinc_ret * get_principal_2(gprinc_arg *argp, CLIENT *clnt) { static gprinc_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, GET_PRINCIPAL, (xdrproc_t) xdr_gprinc_arg, (caddr_t) argp, (xdrproc_t) xdr_gprinc_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } gprincs_ret * get_princs_2(gprincs_arg *argp, CLIENT *clnt) { static gprincs_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, GET_PRINCS, (xdrproc_t) xdr_gprincs_arg, (caddr_t) argp, (xdrproc_t) xdr_gprincs_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } generic_ret * chpass_principal_2(chpass_arg *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, CHPASS_PRINCIPAL, (xdrproc_t) xdr_chpass_arg, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } generic_ret * chpass_principal3_2(chpass3_arg *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, CHPASS_PRINCIPAL3, (xdrproc_t) xdr_chpass3_arg, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } generic_ret * setv4key_principal_2(setv4key_arg *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, SETV4KEY_PRINCIPAL, (xdrproc_t) xdr_setv4key_arg, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } generic_ret * setkey_principal_2(setkey_arg *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, SETKEY_PRINCIPAL, (xdrproc_t) xdr_setkey_arg, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } generic_ret * setkey_principal3_2(setkey3_arg *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, SETKEY_PRINCIPAL3, (xdrproc_t) xdr_setkey3_arg, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } chrand_ret * chrand_principal_2(chrand_arg *argp, CLIENT *clnt) { static chrand_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, CHRAND_PRINCIPAL, (xdrproc_t) xdr_chrand_arg, (caddr_t) argp, (xdrproc_t) xdr_chrand_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } chrand_ret * chrand_principal3_2(chrand3_arg *argp, CLIENT *clnt) { static chrand_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, CHRAND_PRINCIPAL3, (xdrproc_t) xdr_chrand3_arg, (caddr_t) argp, (xdrproc_t) xdr_chrand_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } generic_ret * create_policy_2(cpol_arg *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, CREATE_POLICY, (xdrproc_t) xdr_cpol_arg, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } generic_ret * delete_policy_2(dpol_arg *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, DELETE_POLICY, (xdrproc_t) xdr_dpol_arg, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } generic_ret * modify_policy_2(mpol_arg *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, MODIFY_POLICY, (xdrproc_t) xdr_mpol_arg, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } gpol_ret * get_policy_2(gpol_arg *argp, CLIENT *clnt) { static gpol_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, GET_POLICY, (xdrproc_t) xdr_gpol_arg, (caddr_t) argp, (xdrproc_t) xdr_gpol_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } gpols_ret * get_pols_2(gpols_arg *argp, CLIENT *clnt) { static gpols_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, GET_POLS, (xdrproc_t) xdr_gpols_arg, (caddr_t) argp, (xdrproc_t) xdr_gpols_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } getprivs_ret * get_privs_2(void *argp, CLIENT *clnt) { static getprivs_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, GET_PRIVS, (xdrproc_t) xdr_u_int, (caddr_t) argp, (xdrproc_t) xdr_getprivs_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } generic_ret * init_2(void *argp, CLIENT *clnt) { static generic_ret clnt_res; /* Solaris Kerberos */ if (clnt == NULL) return (NULL); memset((char *)&clnt_res, 0, sizeof(clnt_res)); if (clnt_call(clnt, INIT, (xdrproc_t) xdr_u_int, (caddr_t) argp, (xdrproc_t) xdr_generic_ret, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ #include #include "client_internal.h" kadm5_ret_t kadm5_chpass_principal_util(void *server_handle, krb5_principal princ, char *new_pw, char **ret_pw, char *msg_ret, unsigned int msg_len) { kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); return _kadm5_chpass_principal_util(handle, handle->lhandle, princ, new_pw, ret_pw, msg_ret, msg_len); } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; #endif #include /* SUNWresync121 XXX */ #include #include #include "client_internal.h" #include #include #include kadm5_ret_t kadm5_create_policy(void *server_handle, kadm5_policy_ent_t policy, long mask) { cpol_arg arg; generic_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); if(policy == (kadm5_policy_ent_t) NULL) return EINVAL; arg.mask = mask; arg.api_version = handle->api_version; memcpy(&arg.rec, policy, sizeof(kadm5_policy_ent_rec)); r = create_policy_2(&arg, handle->clnt); if(r == NULL) return KADM5_RPC_ERROR; return r->code; } kadm5_ret_t kadm5_delete_policy(void *server_handle, char *name) { dpol_arg arg; generic_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); if(name == NULL) return EINVAL; arg.name = name; arg.api_version = handle->api_version; r = delete_policy_2(&arg, handle->clnt); if(r == NULL) return KADM5_RPC_ERROR; return r->code; } kadm5_ret_t kadm5_modify_policy(void *server_handle, kadm5_policy_ent_t policy, long mask) { mpol_arg arg; generic_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); if(policy == (kadm5_policy_ent_t) NULL) return EINVAL; arg.mask = mask; arg.api_version = handle->api_version; memcpy(&arg.rec, policy, sizeof(kadm5_policy_ent_rec)); r = modify_policy_2(&arg, handle->clnt); if(r == NULL) return KADM5_RPC_ERROR; return r->code; } kadm5_ret_t kadm5_get_policy(void *server_handle, char *name, kadm5_policy_ent_t ent) { gpol_arg arg; gpol_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); arg.name = name; arg.api_version = handle->api_version; if(name == NULL) return EINVAL; r = get_policy_2(&arg, handle->clnt); if(r == NULL) return KADM5_RPC_ERROR; if (handle->api_version == KADM5_API_VERSION_1) { kadm5_policy_ent_t *entp; entp = (kadm5_policy_ent_t *) ent; if(r->code == 0) { if (!(*entp = (kadm5_policy_ent_t) malloc(sizeof(kadm5_policy_ent_rec)))) return ENOMEM; memcpy(*entp, &r->rec, sizeof(**entp)); } else { *entp = NULL; } } else { if (r->code == 0) memcpy(ent, &r->rec, sizeof(r->rec)); } return r->code; } kadm5_ret_t kadm5_get_policies(void *server_handle, char *exp, char ***pols, int *count) { gpols_arg arg; gpols_ret *r; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); if(pols == NULL || count == NULL) return EINVAL; arg.exp = exp; arg.api_version = handle->api_version; r = get_pols_2(&arg, handle->clnt); if(r == NULL) return KADM5_RPC_ERROR; if(r->code == 0) { *count = r->count; *pols = r->pols; } else { *count = 0; *pols = NULL; } return r->code; } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. * * $Id: clnt_privs.c 18130 2006-06-14 21:42:02Z raeburn $ * $Source$ * */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; #endif #include /* SUNWresync121 XXX */ #include #include #include "client_internal.h" kadm5_ret_t kadm5_get_privs(void *server_handle, long *privs) { getprivs_ret *r; kadm5_server_handle_t handle = server_handle; r = get_privs_2(&handle->api_version, handle->clnt); if (r == NULL) return KADM5_RPC_ERROR; else if (r->code == KADM5_OK) *privs = r->privs; return r->code; } /* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _IPROP_H_RPCGEN #define _IPROP_H_RPCGEN #include typedef struct { u_int utf8str_t_len; char *utf8str_t_val; } utf8str_t; typedef uint32_t kdb_sno_t; struct kdbe_time_t { uint32_t seconds; uint32_t useconds; }; typedef struct kdbe_time_t kdbe_time_t; struct kdbe_key_t { int32_t k_ver; int32_t k_kvno; struct { u_int k_enctype_len; int32_t *k_enctype_val; } k_enctype; struct { u_int k_contents_len; utf8str_t *k_contents_val; } k_contents; }; typedef struct kdbe_key_t kdbe_key_t; struct kdbe_data_t { int32_t k_magic; utf8str_t k_data; }; typedef struct kdbe_data_t kdbe_data_t; struct kdbe_princ_t { utf8str_t k_realm; struct { u_int k_components_len; kdbe_data_t *k_components_val; } k_components; int32_t k_nametype; }; typedef struct kdbe_princ_t kdbe_princ_t; struct kdbe_tl_t { int16_t tl_type; struct { u_int tl_data_len; char *tl_data_val; } tl_data; }; typedef struct kdbe_tl_t kdbe_tl_t; typedef struct { u_int kdbe_pw_hist_t_len; kdbe_key_t *kdbe_pw_hist_t_val; } kdbe_pw_hist_t; enum kdbe_attr_type_t { AT_ATTRFLAGS = 0, AT_MAX_LIFE = 1, AT_MAX_RENEW_LIFE = 2, AT_EXP = 3, AT_PW_EXP = 4, AT_LAST_SUCCESS = 5, AT_LAST_FAILED = 6, AT_FAIL_AUTH_COUNT = 7, AT_PRINC = 8, AT_KEYDATA = 9, AT_TL_DATA = 10, AT_LEN = 11, AT_MOD_PRINC = 12, AT_MOD_TIME = 13, AT_MOD_WHERE = 14, AT_PW_LAST_CHANGE = 15, AT_PW_POLICY = 16, AT_PW_POLICY_SWITCH = 17, AT_PW_HIST_KVNO = 18, AT_PW_HIST = 19 }; typedef enum kdbe_attr_type_t kdbe_attr_type_t; struct kdbe_val_t { kdbe_attr_type_t av_type; union { uint32_t av_attrflags; uint32_t av_max_life; uint32_t av_max_renew_life; uint32_t av_exp; uint32_t av_pw_exp; uint32_t av_last_success; uint32_t av_last_failed; uint32_t av_fail_auth_count; kdbe_princ_t av_princ; struct { u_int av_keydata_len; kdbe_key_t *av_keydata_val; } av_keydata; struct { u_int av_tldata_len; kdbe_tl_t *av_tldata_val; } av_tldata; int16_t av_len; uint32_t av_pw_last_change; kdbe_princ_t av_mod_princ; uint32_t av_mod_time; utf8str_t av_mod_where; utf8str_t av_pw_policy; bool_t av_pw_policy_switch; uint32_t av_pw_hist_kvno; struct { u_int av_pw_hist_len; kdbe_pw_hist_t *av_pw_hist_val; } av_pw_hist; struct { u_int av_extension_len; char *av_extension_val; } av_extension; } kdbe_val_t_u; }; typedef struct kdbe_val_t kdbe_val_t; typedef struct { u_int kdbe_t_len; kdbe_val_t *kdbe_t_val; } kdbe_t; struct kdb_incr_update_t { utf8str_t kdb_princ_name; kdb_sno_t kdb_entry_sno; kdbe_time_t kdb_time; kdbe_t kdb_update; bool_t kdb_deleted; bool_t kdb_commit; struct { u_int kdb_kdcs_seen_by_len; utf8str_t *kdb_kdcs_seen_by_val; } kdb_kdcs_seen_by; struct { u_int kdb_futures_len; char *kdb_futures_val; } kdb_futures; }; typedef struct kdb_incr_update_t kdb_incr_update_t; typedef struct { u_int kdb_ulog_t_len; kdb_incr_update_t *kdb_ulog_t_val; } kdb_ulog_t; enum update_status_t { UPDATE_OK = 0, UPDATE_ERROR = 1, UPDATE_FULL_RESYNC_NEEDED = 2, UPDATE_BUSY = 3, UPDATE_NIL = 4, UPDATE_PERM_DENIED = 5 }; typedef enum update_status_t update_status_t; struct kdb_last_t { kdb_sno_t last_sno; kdbe_time_t last_time; }; typedef struct kdb_last_t kdb_last_t; struct kdb_incr_result_t { kdb_last_t lastentry; kdb_ulog_t updates; update_status_t ret; }; typedef struct kdb_incr_result_t kdb_incr_result_t; struct kdb_fullresync_result_t { kdb_last_t lastentry; update_status_t ret; }; typedef struct kdb_fullresync_result_t kdb_fullresync_result_t; #define KRB5_IPROP_PROG 100423 #define KRB5_IPROP_VERS 1 #define IPROP_NULL 0 extern void * iprop_null_1(); #define IPROP_GET_UPDATES 1 extern kdb_incr_result_t * iprop_get_updates_1(); #define IPROP_FULL_RESYNC 2 extern kdb_fullresync_result_t * iprop_full_resync_1(); extern int krb5_iprop_prog_1_freeresult(); /* the xdr functions */ extern bool_t xdr_utf8str_t(); extern bool_t xdr_kdb_sno_t(); extern bool_t xdr_kdbe_time_t(); extern bool_t xdr_kdbe_key_t(); extern bool_t xdr_kdbe_data_t(); extern bool_t xdr_kdbe_princ_t(); extern bool_t xdr_kdbe_tl_t(); extern bool_t xdr_kdbe_pw_hist_t(); extern bool_t xdr_kdbe_attr_type_t(); extern bool_t xdr_kdbe_val_t(); extern bool_t xdr_kdbe_t(); extern bool_t xdr_kdb_incr_update_t(); extern bool_t xdr_kdb_ulog_t(); extern bool_t xdr_update_status_t(); extern bool_t xdr_kdb_last_t(); extern bool_t xdr_kdb_incr_result_t(); extern bool_t xdr_kdb_fullresync_result_t(); #endif /* !_IPROP_H_RPCGEN */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * lib/kadm/logger.c * * Copyright 1995 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ /* KADM5 wants non-syslog log files to contain syslog-like entries */ #define VERBOSE_LOGS /* * logger.c - Handle logging functions for those who want it. */ #include "k5-int.h" #include "adm_proto.h" #include "com_err.h" #include #include #include #ifdef HAVE_SYSLOG_H #include #endif /* HAVE_SYSLOG_H */ #ifdef HAVE_STDARG_H #include #else /* HAVE_STDARG_H */ #include #endif /* HAVE_STDARG_H */ #include #include #include #define KRB5_KLOG_MAX_ERRMSG_SIZE 2048 #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 256 #endif /* MAXHOSTNAMELEN */ #define LSPEC_PARSE_ERR_1 1 #define LSPEC_PARSE_ERR_2 2 #define LOG_FILE_ERR 3 #define LOG_DEVICE_ERR 4 #define LOG_UFO_STRING 5 #define LOG_EMERG_STRING 6 #define LOG_ALERT_STRING 7 #define LOG_CRIT_STRING 8 #define LOG_ERR_STRING 9 #define LOG_WARNING_STRING 10 #define LOG_NOTICE_STRING 11 #define LOG_INFO_STRING 12 #define LOG_DEBUG_STRING 13 /* This is to assure that we have at least one match in the syslog stuff */ /* static const char LSPEC_PARSE_ERR_1[] = "%s: cannot parse <%s>\n"; static const char LSPEC_PARSE_ERR_2[] = "%s: warning - logging entry syntax error\n"; static const char LOG_FILE_ERR[] = "%s: error writing to %s\n"; static const char LOG_DEVICE_ERR[] = "%s: error writing to %s device\n"; static const char LOG_UFO_STRING[] = "???"; static const char LOG_EMERG_STRING[] = "EMERGENCY"; static const char LOG_ALERT_STRING[] = "ALERT"; static const char LOG_CRIT_STRING[] = "CRITICAL"; static const char LOG_ERR_STRING[] = "Error"; static const char LOG_WARNING_STRING[] = "Warning"; static const char LOG_NOTICE_STRING[] = "Notice"; static const char LOG_INFO_STRING[] = "info"; static const char LOG_DEBUG_STRING[] = "debug"; */ const char * krb5_log_error_table(long errorno) { switch (errorno) { case LSPEC_PARSE_ERR_1: return(gettext("%s: cannot parse <%s>\n")); case LSPEC_PARSE_ERR_2: return(gettext("%s: warning - logging entry syntax error\n")); case LOG_FILE_ERR: return(gettext("%s: error writing to %s\n")); case LOG_DEVICE_ERR: return(gettext("%s: error writing to %s device\n")); case LOG_UFO_STRING: return(gettext("???")); case LOG_EMERG_STRING: return(gettext("EMERGENCY")); case LOG_ALERT_STRING: return(gettext("ALERT")); case LOG_CRIT_STRING: return(gettext("CRITICAL")); case LOG_ERR_STRING: return(gettext("Error")); case LOG_WARNING_STRING: return(gettext("Warning")); case LOG_NOTICE_STRING: return(gettext("Notice")); case LOG_INFO_STRING: case LOG_DEBUG_STRING: default: return(gettext("info")); } } /* * Output logging. * * Output logging is now controlled by the configuration file. We can specify * the following syntaxes under the [logging]->entity specification. * FILE * SYSLOG[=[:]] * STDERR * CONSOLE * DEVICE= * * Where: * is ":" for open/append, "=" for open/create. * is a valid path name. * is one of: (default = ERR) * EMERG * ALERT * CRIT * ERR * WARNING * NOTICE * INFO * DEBUG * is one of: (default = AUTH) * KERN * USER * MAIL * DAEMON * AUTH * LPR * NEWS * UUCP * AUDIT * CRON * LOCAL0..LOCAL7 * is a valid device specification. */ struct log_entry { enum log_type { K_LOG_FILE, K_LOG_SYSLOG, K_LOG_STDERR, K_LOG_CONSOLE, K_LOG_DEVICE, K_LOG_NONE } log_type; krb5_pointer log_2free; union log_union { struct log_file { FILE *lf_filep; char *lf_fname; char *lf_fopen_mode; /* "a+" or "w" */ #define K_LOG_DEF_FILE_ROTATE_PERIOD -1 /* never */ #define K_LOG_DEF_FILE_ROTATE_VERSIONS 0 /* no versions */ time_t lf_rotate_period; time_t lf_last_rotated; int lf_rotate_versions; } log_file; struct log_syslog { int ls_facility; int ls_severity; } log_syslog; struct log_device { FILE *ld_filep; char *ld_devname; } log_device; } log_union; }; #define lfu_filep log_union.log_file.lf_filep #define lfu_fname log_union.log_file.lf_fname #define lfu_fopen_mode log_union.log_file.lf_fopen_mode #define lfu_rotate_period log_union.log_file.lf_rotate_period #define lfu_last_rotated log_union.log_file.lf_last_rotated #define lfu_rotate_versions log_union.log_file.lf_rotate_versions #define lsu_facility log_union.log_syslog.ls_facility #define lsu_severity log_union.log_syslog.ls_severity #define ldu_filep log_union.log_device.ld_filep #define ldu_devname log_union.log_device.ld_devname struct log_control { struct log_entry *log_entries; int log_nentries; char *log_whoami; char *log_hostname; krb5_boolean log_opened; }; static struct log_control log_control = { (struct log_entry *) NULL, 0, (char *) NULL, (char *) NULL, 0 }; static struct log_entry def_log_entry; /* * These macros define any special processing that needs to happen for * devices. For unix, of course, this is hardly anything. */ #define DEVICE_OPEN(d, m) fopen(d, m) #define CONSOLE_OPEN(m) fopen("/dev/console", m) #define DEVICE_PRINT(f, m) ((fprintf(f, "%s\r\n", m) >= 0) ? \ (fflush(f), 0) : \ -1) #define DEVICE_CLOSE(d) fclose(d) /* * klog_rotate() - roate a log file if we have specified rotation * parameters in krb5.conf. */ static void klog_rotate(struct log_entry *le) { time_t t; int i; char *name_buf1; char *name_buf2; char *old_name; char *new_name; char *tmp; FILE *fp; int num_vers; mode_t old_umask; /* * By default we don't rotate. */ if (le->lfu_rotate_period == K_LOG_DEF_FILE_ROTATE_PERIOD) return; t = time(0); if (t >= le->lfu_last_rotated + le->lfu_rotate_period) { /* * The N log file versions will be renamed X.N-1 X.N-2, ... X.0. * So the allocate file name buffers that can the version * number extensions. * 32 extra bytes is plenty. */ name_buf1 = malloc(strlen(le->lfu_fname) + 32); if (name_buf1 == NULL) return; name_buf2 = malloc(strlen(le->lfu_fname) + 32); if (name_buf2 == NULL) { free(name_buf1); return; } old_name = name_buf1; new_name = name_buf2; /* * If there N versions, then the first one has file extension * of N-1. */ (void) sprintf(new_name, "%s.%d", le->lfu_fname, le->lfu_rotate_versions - 1); /* * Rename file.N-2 to file.N-1, file.N-3 to file.N-2, ... * file.0 to file.1 */ for (i = le->lfu_rotate_versions - 1; i > 0; i--) { (void) sprintf(old_name, "%s.%d", le->lfu_fname, i - 1); (void) rename(old_name, new_name); /* * swap old name and new name. This way, * on the next iteration, new_name.X * becomes new_name.X-1. */ tmp = old_name; old_name = new_name; new_name = tmp; } old_name = le->lfu_fname; (void) rename(old_name, new_name); /* * Even though we don't know yet if the fopen() * of the log file will succeed, we mark the log * as rotated. This is so we don't repeatably * rotate file.N-2 to file.N-1 ... etc without * waiting for the rotate period to elapse. */ le->lfu_last_rotated = t; /* * Default log file creation mode should be read-only * by owner(root), but the admin can override with * chmod(1) if desired. */ old_umask = umask(077); fp = fopen(old_name, le->lfu_fopen_mode); umask(old_umask); if (fp != NULL) { (void) fclose(le->lfu_filep); le->lfu_filep = fp; /* * If the version parameter in krb5.conf was * 0, then we take this to mean that rotating the * log file will cause us to dispose of the * old one, and created a new one. We have just * renamed the old one to file.-1, so remove it. */ if (le->lfu_rotate_versions <= 0) (void) unlink(new_name); } else { fprintf(stderr, gettext("During rotate, couldn't open log file %s: %s\n"), old_name, error_message(errno)); /* * Put it back. */ (void) rename(new_name, old_name); } free(name_buf1); free(name_buf2); } } /* * klog_com_err_proc() - Handle com_err(3) messages as specified by the * profile. */ static krb5_context err_context; static void klog_com_err_proc(const char *whoami, long code, const char *format, va_list ap) { char outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE]; int lindex; const char *actual_format; #ifdef HAVE_SYSLOG int log_pri = -1; #endif /* HAVE_SYSLOG */ char *cp; char *syslogp; /* Make the header */ sprintf(outbuf, "%s: ", whoami); /* * Squirrel away address after header for syslog since syslog makes * a header */ syslogp = &outbuf[strlen(outbuf)]; /* If reporting an error message, separate it. */ if (code) { const char *emsg; outbuf[sizeof(outbuf) - 1] = '\0'; emsg = krb5_get_error_message (err_context, code); strncat(outbuf, emsg, sizeof(outbuf) - 1 - strlen(outbuf)); strncat(outbuf, " - ", sizeof(outbuf) - 1 - strlen(outbuf)); krb5_free_error_message(err_context, emsg); } cp = &outbuf[strlen(outbuf)]; actual_format = format; #ifdef HAVE_SYSLOG /* * This is an unpleasant hack. If the first character is less than * 8, then we assume that it is a priority. * * Since it is not guaranteed that there is a direct mapping between * syslog priorities (e.g. Ultrix and old BSD), we resort to this * intermediate representation. */ if ((((unsigned char) *format) > 0) && (((unsigned char) *format) <= 8)) { actual_format = (format + 1); switch ((unsigned char) *format) { #ifdef LOG_EMERG case 1: log_pri = LOG_EMERG; break; #endif /* LOG_EMERG */ #ifdef LOG_ALERT case 2: log_pri = LOG_ALERT; break; #endif /* LOG_ALERT */ #ifdef LOG_CRIT case 3: log_pri = LOG_CRIT; break; #endif /* LOG_CRIT */ default: case 4: log_pri = LOG_ERR; break; #ifdef LOG_WARNING case 5: log_pri = LOG_WARNING; break; #endif /* LOG_WARNING */ #ifdef LOG_NOTICE case 6: log_pri = LOG_NOTICE; break; #endif /* LOG_NOTICE */ #ifdef LOG_INFO case 7: log_pri = LOG_INFO; break; #endif /* LOG_INFO */ #ifdef LOG_DEBUG case 8: log_pri = LOG_DEBUG; break; #endif /* LOG_DEBUG */ } } #endif /* HAVE_SYSLOG */ /* Now format the actual message */ #if HAVE_VSNPRINTF vsnprintf(cp, sizeof(outbuf) - (cp - outbuf), actual_format, ap); #elif HAVE_VSPRINTF vsprintf(cp, actual_format, ap); #else /* HAVE_VSPRINTF */ sprintf(cp, actual_format, ((int *) ap)[0], ((int *) ap)[1], ((int *) ap)[2], ((int *) ap)[3], ((int *) ap)[4], ((int *) ap)[5]); #endif /* HAVE_VSPRINTF */ /* * Now that we have the message formatted, perform the output to each * logging specification. */ for (lindex = 0; lindex < log_control.log_nentries; lindex++) { switch (log_control.log_entries[lindex].log_type) { case K_LOG_FILE: klog_rotate(&log_control.log_entries[lindex]); /*FALLTHRU*/ case K_LOG_STDERR: /* * Files/standard error. */ if (fprintf(log_control.log_entries[lindex].lfu_filep, "%s\n", outbuf) < 0) { /* Attempt to report error */ fprintf(stderr, krb5_log_error_table(LOG_FILE_ERR), whoami, log_control.log_entries[lindex].lfu_fname); } else { fflush(log_control.log_entries[lindex].lfu_filep); } break; case K_LOG_CONSOLE: case K_LOG_DEVICE: /* * Devices (may need special handling) */ if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep, outbuf) < 0) { /* Attempt to report error */ fprintf(stderr, krb5_log_error_table(LOG_DEVICE_ERR), whoami, log_control.log_entries[lindex].ldu_devname); } break; #ifdef HAVE_SYSLOG case K_LOG_SYSLOG: /* * System log. */ /* * If we have specified a priority through our hackery, then * use it, otherwise use the default. */ if (log_pri >= 0) log_pri |= log_control.log_entries[lindex].lsu_facility; else log_pri = log_control.log_entries[lindex].lsu_facility | log_control.log_entries[lindex].lsu_severity; /* Log the message with our header trimmed off */ syslog(log_pri, syslogp); break; #endif /* HAVE_SYSLOG */ default: break; } } } /* * krb5_klog_init() - Initialize logging. * * This routine parses the syntax described above to specify destinations for * com_err(3) or krb5_klog_syslog() messages generated by the caller. * * Parameters: * kcontext - Kerberos context. * ename - Entity name as it is to appear in the profile. * whoami - Entity name as it is to appear in error output. * do_com_err - Take over com_err(3) processing. * * Implicit inputs: * stderr - This is where STDERR output goes. * * Implicit outputs: * log_nentries - Number of log entries, both valid and invalid. * log_control - List of entries (log_nentries long) which contains * data for klog_com_err_proc() to use to determine * where/how to send output. */ krb5_error_code krb5_klog_init(krb5_context kcontext, char *ename, char *whoami, krb5_boolean do_com_err) { const char *logging_profent[3]; const char *logging_defent[3]; char **logging_specs; int i, ngood; char *cp, *cp2; char savec = '\0'; int error; int do_openlog, log_facility; FILE *f; mode_t old_umask; /* Initialize */ do_openlog = 0; log_facility = 0; err_context = kcontext; /* * Look up [logging]-> in the profile. If that doesn't * succeed, then look for [logging]->default. */ logging_profent[0] = "logging"; logging_profent[1] = ename; logging_profent[2] = (char *) NULL; logging_defent[0] = "logging"; logging_defent[1] = "default"; logging_defent[2] = (char *) NULL; logging_specs = (char **) NULL; ngood = 0; log_control.log_nentries = 0; if (!profile_get_values(kcontext->profile, logging_profent, &logging_specs) || !profile_get_values(kcontext->profile, logging_defent, &logging_specs)) { /* * We have a match, so we first count the number of elements */ for (log_control.log_nentries = 0; logging_specs[log_control.log_nentries]; log_control.log_nentries++); /* * Now allocate our structure. */ log_control.log_entries = (struct log_entry *) malloc(log_control.log_nentries * sizeof(struct log_entry)); if (log_control.log_entries) { /* * Scan through the list. */ for (i=0; i * so, trim off the leading and trailing whitespace here. */ for (cp = logging_specs[i]; isspace((int) *cp); cp++); for (cp2 = &logging_specs[i][strlen(logging_specs[i])-1]; isspace((int) *cp2); cp2--); cp2++; *cp2 = '\0'; /* * Is this a file? */ if (!strncasecmp(cp, "FILE", 4)) { /* * Check for append/overwrite, then open the file. */ if (cp[4] == ':' || cp[4] == '=') { log_control.log_entries[i].lfu_fopen_mode = (cp[4] == ':') ? "a+F" : "wF"; old_umask = umask(077); f = fopen(&cp[5], log_control.log_entries[i].lfu_fopen_mode); umask(old_umask); if (f) { char rotate_kw[128]; log_control.log_entries[i].lfu_filep = f; log_control.log_entries[i].log_type = K_LOG_FILE; log_control.log_entries[i].lfu_fname = &cp[5]; log_control.log_entries[i].lfu_rotate_period = K_LOG_DEF_FILE_ROTATE_PERIOD; log_control.log_entries[i].lfu_rotate_versions = K_LOG_DEF_FILE_ROTATE_VERSIONS; log_control.log_entries[i].lfu_last_rotated = time(0); /* * Now parse for ename_"rotate" = { * period = XXX * versions = 10 * } */ if (strlen(ename) + strlen("_rotate") < sizeof (rotate_kw)) { char *time; krb5_deltat dt; int vers; strcpy(rotate_kw, ename); strcat(rotate_kw, "_rotate"); if (!profile_get_string(kcontext->profile, "logging", rotate_kw, "period", NULL, &time)) { if (time != NULL) { if (!krb5_string_to_deltat(time, &dt)) { log_control.log_entries[i].lfu_rotate_period = (time_t) dt; } free(time); } } if (!profile_get_integer( kcontext->profile, "logging", rotate_kw, "versions", K_LOG_DEF_FILE_ROTATE_VERSIONS, &vers)) { log_control.log_entries[i].lfu_rotate_versions = vers; } } } else { fprintf(stderr, gettext("Couldn't open log file %s: %s\n"), &cp[5], error_message(errno)); continue; } } } #ifdef HAVE_SYSLOG /* * Is this a syslog? */ else if (!strncasecmp(cp, "SYSLOG", 6)) { error = 0; log_control.log_entries[i].lsu_facility = LOG_AUTH; log_control.log_entries[i].lsu_severity = LOG_ERR; /* * Is there a severify specified? */ if (cp[6] == ':') { /* * Find the end of the severity. */ cp2 = strchr(&cp[7], ':'); if (cp2) { savec = *cp2; *cp2 = '\0'; cp2++; } /* * Match a severity. */ if (!strcasecmp(&cp[7], "ERR")) { log_control.log_entries[i].lsu_severity = LOG_ERR; } #ifdef LOG_EMERG else if (!strcasecmp(&cp[7], "EMERG")) { log_control.log_entries[i].lsu_severity = LOG_EMERG; } #endif /* LOG_EMERG */ #ifdef LOG_ALERT else if (!strcasecmp(&cp[7], "ALERT")) { log_control.log_entries[i].lsu_severity = LOG_ALERT; } #endif /* LOG_ALERT */ #ifdef LOG_CRIT else if (!strcasecmp(&cp[7], "CRIT")) { log_control.log_entries[i].lsu_severity = LOG_CRIT; } #endif /* LOG_CRIT */ #ifdef LOG_WARNING else if (!strcasecmp(&cp[7], "WARNING")) { log_control.log_entries[i].lsu_severity = LOG_WARNING; } #endif /* LOG_WARNING */ #ifdef LOG_NOTICE else if (!strcasecmp(&cp[7], "NOTICE")) { log_control.log_entries[i].lsu_severity = LOG_NOTICE; } #endif /* LOG_NOTICE */ #ifdef LOG_INFO else if (!strcasecmp(&cp[7], "INFO")) { log_control.log_entries[i].lsu_severity = LOG_INFO; } #endif /* LOG_INFO */ #ifdef LOG_DEBUG else if (!strcasecmp(&cp[7], "DEBUG")) { log_control.log_entries[i].lsu_severity = LOG_DEBUG; } #endif /* LOG_DEBUG */ else error = 1; /* * If there is a facility present, then parse that. */ if (cp2) { if (!strcasecmp(cp2, "AUTH")) { log_control.log_entries[i].lsu_facility = LOG_AUTH; } else if (!strcasecmp(cp2, "KERN")) { log_control.log_entries[i].lsu_facility = LOG_KERN; } else if (!strcasecmp(cp2, "USER")) { log_control.log_entries[i].lsu_facility = LOG_USER; } else if (!strcasecmp(cp2, "MAIL")) { log_control.log_entries[i].lsu_facility = LOG_MAIL; } else if (!strcasecmp(cp2, "DAEMON")) { log_control.log_entries[i].lsu_facility = LOG_DAEMON; } else if (!strcasecmp(cp2, "LPR")) { log_control.log_entries[i].lsu_facility = LOG_LPR; } else if (!strcasecmp(cp2, "NEWS")) { log_control.log_entries[i].lsu_facility = LOG_NEWS; } else if (!strcasecmp(cp2, "UUCP")) { log_control.log_entries[i].lsu_facility = LOG_UUCP; } else if (!strcasecmp(cp2, "CRON")) { log_control.log_entries[i].lsu_facility = LOG_CRON; } else if (!strcasecmp(cp2, "AUDIT")) { log_control.log_entries[i].lsu_facility = LOG_AUDIT; } else if (!strcasecmp(cp2, "LOCAL0")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL0; } else if (!strcasecmp(cp2, "LOCAL1")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL1; } else if (!strcasecmp(cp2, "LOCAL2")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL2; } else if (!strcasecmp(cp2, "LOCAL3")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL3; } else if (!strcasecmp(cp2, "LOCAL4")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL4; } else if (!strcasecmp(cp2, "LOCAL5")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL5; } else if (!strcasecmp(cp2, "LOCAL6")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL6; } else if (!strcasecmp(cp2, "LOCAL7")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL7; } cp2--; *cp2 = savec; } } if (!error) { log_control.log_entries[i].log_type = K_LOG_SYSLOG; do_openlog = 1; log_facility = log_control.log_entries[i].lsu_facility; } } #endif /* HAVE_SYSLOG */ /* * Is this a standard error specification? */ else if (!strcasecmp(cp, "STDERR")) { log_control.log_entries[i].lfu_filep = fdopen(fileno(stderr), "a+F"); if (log_control.log_entries[i].lfu_filep) { log_control.log_entries[i].log_type = K_LOG_STDERR; log_control.log_entries[i].lfu_fname = "standard error"; } } /* * Is this a specification of the console? */ else if (!strcasecmp(cp, "CONSOLE")) { log_control.log_entries[i].ldu_filep = CONSOLE_OPEN("a+F"); if (log_control.log_entries[i].ldu_filep) { log_control.log_entries[i].log_type = K_LOG_CONSOLE; log_control.log_entries[i].ldu_devname = "console"; } } /* * Is this a specification of a device? */ else if (!strncasecmp(cp, "DEVICE", 6)) { /* * We handle devices very similarly to files. */ if (cp[6] == '=') { log_control.log_entries[i].ldu_filep = DEVICE_OPEN(&cp[7], "wF"); if (log_control.log_entries[i].ldu_filep) { log_control.log_entries[i].log_type = K_LOG_DEVICE; log_control.log_entries[i].ldu_devname = &cp[7]; } } } /* * See if we successfully parsed this specification. */ if (log_control.log_entries[i].log_type == K_LOG_NONE) { fprintf(stderr, krb5_log_error_table(LSPEC_PARSE_ERR_1), whoami, cp); fprintf(stderr, krb5_log_error_table(LSPEC_PARSE_ERR_2), whoami); } else ngood++; } } /* * If we didn't find anything, then free our lists. */ if (ngood == 0) { for (i=0; ilog_type = K_LOG_SYSLOG; log_control.log_entries->log_2free = (krb5_pointer) NULL; log_facility = log_control.log_entries->lsu_facility = LOG_AUTH; log_control.log_entries->lsu_severity = LOG_ERR; do_openlog = 1; log_control.log_nentries = 1; } if (log_control.log_nentries) { log_control.log_whoami = (char *) malloc(strlen(whoami)+1); if (log_control.log_whoami) strcpy(log_control.log_whoami, whoami); log_control.log_hostname = (char *) malloc(MAXHOSTNAMELEN + 1); if (log_control.log_hostname) { gethostname(log_control.log_hostname, MAXHOSTNAMELEN); log_control.log_hostname[MAXHOSTNAMELEN] = '\0'; } #ifdef HAVE_OPENLOG if (do_openlog) { openlog(whoami, LOG_NDELAY|LOG_PID, log_facility); log_control.log_opened = 1; } #endif /* HAVE_OPENLOG */ if (do_com_err) (void) set_com_err_hook(klog_com_err_proc); } return((log_control.log_nentries) ? 0 : ENOENT); } /* * krb5_klog_close() - Close the logging context and free all data. */ void krb5_klog_close(krb5_context kcontext) { int lindex; (void) reset_com_err_hook(); for (lindex = 0; lindex < log_control.log_nentries; lindex++) { switch (log_control.log_entries[lindex].log_type) { case K_LOG_FILE: case K_LOG_STDERR: /* * Files/standard error. */ fclose(log_control.log_entries[lindex].lfu_filep); break; case K_LOG_CONSOLE: case K_LOG_DEVICE: /* * Devices (may need special handling) */ DEVICE_CLOSE(log_control.log_entries[lindex].ldu_filep); break; #ifdef HAVE_SYSLOG case K_LOG_SYSLOG: /* * System log. */ break; #endif /* HAVE_SYSLOG */ default: break; } if (log_control.log_entries[lindex].log_2free) free(log_control.log_entries[lindex].log_2free); } if (log_control.log_entries != &def_log_entry) free(log_control.log_entries); log_control.log_entries = (struct log_entry *) NULL; log_control.log_nentries = 0; if (log_control.log_whoami) free(log_control.log_whoami); log_control.log_whoami = (char *) NULL; if (log_control.log_hostname) free(log_control.log_hostname); log_control.log_hostname = (char *) NULL; #ifdef HAVE_CLOSELOG if (log_control.log_opened) closelog(); #endif /* HAVE_CLOSELOG */ } /* * severity2string() - Convert a severity to a string. */ static const char * severity2string(int severity) { int s; const char *ss; s = severity & LOG_PRIMASK; ss = krb5_log_error_table(LOG_UFO_STRING); switch (s) { #ifdef LOG_EMERG case LOG_EMERG: ss = krb5_log_error_table(LOG_EMERG_STRING); break; #endif /* LOG_EMERG */ #ifdef LOG_ALERT case LOG_ALERT: ss = krb5_log_error_table(LOG_ALERT_STRING); break; #endif /* LOG_ALERT */ #ifdef LOG_CRIT case LOG_CRIT: ss = krb5_log_error_table(LOG_CRIT_STRING); break; #endif /* LOG_CRIT */ case LOG_ERR: ss = krb5_log_error_table(LOG_ERR_STRING); break; #ifdef LOG_WARNING case LOG_WARNING: ss = krb5_log_error_table(LOG_WARNING_STRING); break; #endif /* LOG_WARNING */ #ifdef LOG_NOTICE case LOG_NOTICE: ss = krb5_log_error_table(LOG_NOTICE_STRING); break; #endif /* LOG_NOTICE */ #ifdef LOG_INFO case LOG_INFO: ss = krb5_log_error_table(LOG_INFO_STRING); break; #endif /* LOG_INFO */ #ifdef LOG_DEBUG case LOG_DEBUG: ss = krb5_log_error_table(LOG_DEBUG_STRING); break; #endif /* LOG_DEBUG */ } return((char *) ss); } /* * krb5_klog_syslog() - Simulate the calling sequence of syslog(3), while * also performing the logging redirection as specified * by krb5_klog_init(). */ static int klog_vsyslog(int priority, const char *format, va_list arglist) { char outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE]; int lindex; char *syslogp; char *cp; time_t now; #ifdef HAVE_STRFTIME size_t soff; #endif /* HAVE_STRFTIME */ /* * Format a syslog-esque message of the format: * * (verbose form) * [](): * * (short form) * */ cp = outbuf; (void) time(&now); #ifdef HAVE_STRFTIME /* * Format the date: mon dd hh:mm:ss */ soff = strftime(outbuf, sizeof(outbuf), "%b %d %H:%M:%S", localtime(&now)); if (soff > 0) cp += soff; else return(-1); #else /* HAVE_STRFTIME */ /* * Format the date: * We ASSUME here that the output of ctime is of the format: * dow mon dd hh:mm:ss tzs yyyy\n * 012345678901234567890123456789 */ strncpy(outbuf, ctime(&now) + 4, 15); cp += 15; #endif /* HAVE_STRFTIME */ #ifdef VERBOSE_LOGS sprintf(cp, " %s %s[%ld](%s): ", log_control.log_hostname, log_control.log_whoami, (long) getpid(), severity2string(priority)); #else sprintf(cp, " "); #endif syslogp = &outbuf[strlen(outbuf)]; /* Now format the actual message */ #ifdef HAVE_VSNPRINTF vsnprintf(syslogp, sizeof(outbuf) - (syslogp - outbuf), format, arglist); #elif HAVE_VSPRINTF vsprintf(syslogp, format, arglist); #else /* HAVE_VSPRINTF */ sprintf(syslogp, format, ((int *) arglist)[0], ((int *) arglist)[1], ((int *) arglist)[2], ((int *) arglist)[3], ((int *) arglist)[4], ((int *) arglist)[5]); #endif /* HAVE_VSPRINTF */ /* * If the user did not use krb5_klog_init() instead of dropping * the request on the floor, syslog it - if it exists */ #ifdef HAVE_SYSLOG if (log_control.log_nentries == 0) { /* Log the message with our header trimmed off */ syslog(priority, "%s", syslogp); } #endif /* * Now that we have the message formatted, perform the output to each * logging specification. */ for (lindex = 0; lindex < log_control.log_nentries; lindex++) { switch (log_control.log_entries[lindex].log_type) { case K_LOG_FILE: klog_rotate(&log_control.log_entries[lindex]); /*FALLTHRU*/ case K_LOG_STDERR: /* * Files/standard error. */ if (fprintf(log_control.log_entries[lindex].lfu_filep, "%s\n", outbuf) < 0) { /* Attempt to report error */ fprintf(stderr, krb5_log_error_table(LOG_FILE_ERR), log_control.log_whoami, log_control.log_entries[lindex].lfu_fname); } else { fflush(log_control.log_entries[lindex].lfu_filep); } break; case K_LOG_CONSOLE: case K_LOG_DEVICE: /* * Devices (may need special handling) */ if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep, outbuf) < 0) { /* Attempt to report error */ fprintf(stderr, krb5_log_error_table(LOG_DEVICE_ERR), log_control.log_whoami, log_control.log_entries[lindex].ldu_devname); } break; #ifdef HAVE_SYSLOG case K_LOG_SYSLOG: /* * System log. */ /* Log the message with our header trimmed off */ syslog(priority, "%s", syslogp); break; #endif /* HAVE_SYSLOG */ default: break; } } return(0); } int krb5_klog_syslog(int priority, const char *format, ...) { int retval; va_list pvar; va_start(pvar, format); retval = klog_vsyslog(priority, format, pvar); va_end(pvar); return(retval); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION SUNWprivate_1.1 { global: _kadm5_get_kpasswd_protocol; chpass_principal3_2; chpass_principal_2; chpw_error_message; chrand_principal3_2; chrand_principal_2; create_policy_2; create_principal3_2; create_principal_2; delete_policy_2; delete_principal_2; display_status; get_policy_2; get_pols_2; get_principal_2; get_princs_2; get_privs_2; init_2; kadm5_chpass_principal; kadm5_chpass_principal_3; kadm5_chpass_principal_util; kadm5_chpass_principal_v2; kadm5_create_policy; kadm5_create_principal; kadm5_create_principal_3; kadm5_decrypt_key; kadm5_delete_policy; kadm5_delete_principal; kadm5_destroy; kadm5_flush; kadm5_free_config_params; kadm5_free_name_list; kadm5_free_policy_ent; kadm5_free_principal_ent; kadm5_get_adm_host_srv_name; kadm5_get_admin_service_name; kadm5_get_config_params; kadm5_get_cpw_host_srv_name; kadm5_get_kiprop_host_srv_name; kadm5_get_master; kadm5_get_policies; kadm5_get_policy; kadm5_get_principal; kadm5_get_principals; kadm5_get_privs; kadm5_init; kadm5_init_iprop; kadm5_init_krb5_context; kadm5_init_with_creds; kadm5_init_with_password; kadm5_init_with_skey; kadm5_is_master; kadm5_lock; kadm5_modify_policy; kadm5_modify_principal; kadm5_randkey_principal; kadm5_randkey_principal_3; kadm5_randkey_principal_old; kadm5_rename_principal; kadm5_setkey_principal; kadm5_setkey_principal_3; kadm5_unlock; krb5_aprof_finish; krb5_aprof_get_boolean; krb5_aprof_get_deltat; krb5_aprof_get_int32; krb5_aprof_get_string; krb5_aprof_getvals; krb5_aprof_init; krb5_flags_to_string; krb5_free_key_data_contents; krb5_free_realm_params; krb5_input_flag_to_string; krb5_keysalt_is_present; krb5_keysalt_iterate; krb5_klog_close; krb5_klog_init; krb5_klog_syslog; krb5_log_error_table; krb5int_mk_chpw_req; krb5int_rd_chpw_rep; krb5_read_realm_params; krb5_string_to_flags; krb5_string_to_keysalts; modify_policy_2; modify_principal_2; rename_principal_2; setkey_principal3_2; setkey_principal_2; xdr_chpass3_arg; xdr_chpass_arg; xdr_chrand3_arg; xdr_chrand_arg; xdr_chrand_ret; xdr_cpol_arg; xdr_cprinc3_arg; xdr_cprinc_arg; xdr_dpol_arg; xdr_dprinc_arg; xdr_generic_ret; xdr_getprivs_ret; xdr_gpol_arg; xdr_gpol_ret; xdr_gpols_arg; xdr_gpols_ret; xdr_gprinc_arg; xdr_gprinc_ret; xdr_gprincs_arg; xdr_gprincs_ret; xdr_kadm5_policy_ent_rec; xdr_kadm5_principal_ent_rec; xdr_kadm5_principal_ent_rec_v1; xdr_kadm5_ret_t; xdr_krb5_deltat; xdr_krb5_enctype; xdr_krb5_flags; xdr_krb5_int16; xdr_krb5_key_data_nocontents; xdr_krb5_key_salt_tuple; xdr_krb5_keyblock; xdr_krb5_kvno; xdr_krb5_octet; xdr_krb5_principal; xdr_krb5_salttype; xdr_krb5_timestamp; xdr_krb5_tl_data; xdr_krb5_ui_2; xdr_krb5_ui_4; xdr_mpol_arg; xdr_mprinc_arg; xdr_nullstring; xdr_nulltype; xdr_rprinc_arg; xdr_setkey3_arg; xdr_setkey_arg; xdr_ui_4; local: *; }; /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * kadm_err.h: * This file is automatically generated; please do not edit it. */ #define KADM5_FAILURE (43787520L) #define KADM5_AUTH_GET (43787521L) #define KADM5_AUTH_ADD (43787522L) #define KADM5_AUTH_MODIFY (43787523L) #define KADM5_AUTH_DELETE (43787524L) #define KADM5_AUTH_INSUFFICIENT (43787525L) #define KADM5_BAD_DB (43787526L) #define KADM5_DUP (43787527L) #define KADM5_RPC_ERROR (43787528L) #define KADM5_NO_SRV (43787529L) #define KADM5_BAD_HIST_KEY (43787530L) #define KADM5_NOT_INIT (43787531L) #define KADM5_UNK_PRINC (43787532L) #define KADM5_UNK_POLICY (43787533L) #define KADM5_BAD_MASK (43787534L) #define KADM5_BAD_CLASS (43787535L) #define KADM5_BAD_LENGTH (43787536L) #define KADM5_BAD_POLICY (43787537L) #define KADM5_BAD_PRINCIPAL (43787538L) #define KADM5_BAD_AUX_ATTR (43787539L) #define KADM5_BAD_HISTORY (43787540L) #define KADM5_BAD_MIN_PASS_LIFE (43787541L) #define KADM5_PASS_Q_TOOSHORT (43787542L) #define KADM5_PASS_Q_CLASS (43787543L) #define KADM5_PASS_Q_DICT (43787544L) #define KADM5_PASS_REUSE (43787545L) #define KADM5_PASS_TOOSOON (43787546L) #define KADM5_POLICY_REF (43787547L) #define KADM5_INIT (43787548L) #define KADM5_BAD_PASSWORD (43787549L) #define KADM5_PROTECT_PRINCIPAL (43787550L) #define KADM5_BAD_SERVER_HANDLE (43787551L) #define KADM5_BAD_STRUCT_VERSION (43787552L) #define KADM5_OLD_STRUCT_VERSION (43787553L) #define KADM5_NEW_STRUCT_VERSION (43787554L) #define KADM5_BAD_API_VERSION (43787555L) #define KADM5_OLD_LIB_API_VERSION (43787556L) #define KADM5_OLD_SERVER_API_VERSION (43787557L) #define KADM5_NEW_LIB_API_VERSION (43787558L) #define KADM5_NEW_SERVER_API_VERSION (43787559L) #define KADM5_SECURE_PRINC_MISSING (43787560L) #define KADM5_NO_RENAME_SALT (43787561L) #define KADM5_BAD_CLIENT_PARAMS (43787562L) #define KADM5_BAD_SERVER_PARAMS (43787563L) #define KADM5_AUTH_LIST (43787564L) #define KADM5_AUTH_CHANGEPW (43787565L) #define KADM5_GSS_ERROR (43787566L) #define KADM5_BAD_TL_TYPE (43787567L) #define KADM5_MISSING_CONF_PARAMS (43787568L) #define KADM5_BAD_SERVER_NAME (43787569L) #define KADM5_AUTH_SETKEY (43787570L) #define KADM5_SETKEY_DUP_ENCTYPES (43787571L) #define KADM5_SETV4KEY_INVAL_ENCTYPE (43787572L) #define KADM5_SETKEY3_ETYPE_MISMATCH (43787573L) #define KADM5_MISSING_KRB5_CONF_PARAMS (43787574L) #define KADM5_RPC_ERROR_CANTENCODEARGS (43787575L) #define KADM5_RPC_ERROR_CANTDECODEARGS (43787576L) #define KADM5_XDR_FAILURE (43787577L) #define ERROR_TABLE_BASE_ovk (43787520L) extern const struct error_table et_ovk_error_table; #if !defined(_WIN32) /* for compatibility with older versions... */ extern void initialize_ovk_error_table (void) /*@modifies internalState@*/; #else #define initialize_ovk_error_table() #endif #if !defined(_WIN32) #define init_ovk_err_tbl initialize_ovk_error_table #define ovk_err_base ERROR_TABLE_BASE_ovk #endif /* * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * lib/kad5/kadm_host_srv_names.c */ #include #include "admin.h" #include #include #define KADM5_MASTER "admin_server" #define KADM5_KPASSWD "kpasswd_server" /* * Find the admin server for the given realm. If the realm is null or * the empty string, find the admin server for the default realm. * Returns 0 on succsess (KADM5_OK). It is the callers responsibility to * free the storage allocated to the admin server, master. */ kadm5_ret_t kadm5_get_master(krb5_context context, const char *realm, char **master) { /* Solaris Kerberos */ char *def_realm = NULL; char *delim; #ifdef KRB5_DNS_LOOKUP struct sockaddr *addrs; int naddrs; unsigned short dns_portno; char dns_host[MAX_DNS_NAMELEN]; krb5_data dns_realm; krb5_error_code dns_ret = 1; #endif /* KRB5_DNS_LOOKUP */ if (realm == 0 || *realm == '\0') krb5_get_default_realm(context, &def_realm); (void) profile_get_string(context->profile, "realms", realm ? realm : def_realm, KADM5_MASTER, 0, master); if ((*master != NULL) && ((delim = strchr(*master, ':')) != NULL)) *delim = '\0'; #ifdef KRB5_DNS_LOOKUP if (*master == NULL) { /* * Initialize realm info for (possible) DNS lookups. */ dns_realm.data = strdup(realm ? realm : def_realm); dns_realm.length = strlen(realm ? realm : def_realm); dns_realm.magic = 0; dns_ret = krb5_get_servername(context, &dns_realm, "_kerberos-adm", "_udp", dns_host, &dns_portno); if (dns_ret == 0) *master = strdup(dns_host); if (dns_realm.data) free(dns_realm.data); } #endif /* KRB5_DNS_LOOKUP */ /* Solaris Kerberos */ if (def_realm != NULL) krb5_free_default_realm(context, def_realm); return (*master ? KADM5_OK : KADM5_NO_SRV); } /* * Find the kpasswd server for the given realm. If the realm is null or * the empty string, find the admin server for the default realm. * Returns 0 on succsess (KADM5_OK). It is the callers responsibility to * free the storage allocated to the admin server, master. */ kadm5_ret_t kadm5_get_kpasswd(krb5_context context, const char *realm, char **kpasswd) { char *def_realm = NULL; char *delim; #ifdef KRB5_DNS_LOOKUP struct sockaddr *addrs; int naddrs; unsigned short dns_portno; char dns_host[MAX_DNS_NAMELEN]; krb5_data dns_realm; krb5_error_code dns_ret = 1, ret; #endif /* KRB5_DNS_LOOKUP */ if (realm == 0 || *realm == '\0') { ret = krb5_get_default_realm(context, &def_realm); if (ret != 0) return (ret); } (void) profile_get_string(context->profile, "realms", realm ? realm : def_realm, KADM5_KPASSWD, 0, kpasswd); if ((*kpasswd != NULL) && ((delim = strchr(*kpasswd, ':')) != NULL)) *delim = '\0'; #ifdef KRB5_DNS_LOOKUP if (*kpasswd == NULL) { /* * Initialize realm info for (possible) DNS lookups. */ dns_realm.data = strdup(realm ? realm : def_realm); if (dns_realm.data == NULL) { if (def_realm != NULL) free(def_realm); return (ENOMEM); } dns_realm.length = strlen(realm ? realm : def_realm); dns_realm.magic = 0; dns_ret = krb5_get_servername(context, &dns_realm, "_kpasswd", "_tcp", dns_host, &dns_portno); if (dns_ret == 0) { *kpasswd = strdup(dns_host); if (*kpasswd == NULL) { free(dns_realm.data); if (def_realm != NULL) free(def_realm); return (ENOMEM); } } free(dns_realm.data); } #endif /* KRB5_DNS_LOOKUP */ if (def_realm != NULL) free(def_realm); return (*kpasswd ? KADM5_OK : KADM5_NO_SRV); } /* * Get the host base service name for the admin principal. Returns * KADM5_OK on success. Caller must free the storage allocated for * host_service_name. */ kadm5_ret_t kadm5_get_adm_host_srv_name(krb5_context context, const char *realm, char **host_service_name) { kadm5_ret_t ret; char *name; char *host; if (ret = kadm5_get_master(context, realm, &host)) return (ret); name = malloc(strlen(KADM5_ADMIN_HOST_SERVICE)+ strlen(host) + 2); if (name == NULL) { free(host); return (ENOMEM); } sprintf(name, "%s@%s", KADM5_ADMIN_HOST_SERVICE, host); free(host); *host_service_name = name; return (KADM5_OK); } /* * Get the host base service name for the changepw principal. Returns * KADM5_OK on success. Caller must free the storage allocated for * host_service_name. */ kadm5_ret_t kadm5_get_cpw_host_srv_name(krb5_context context, const char *realm, char **host_service_name) { kadm5_ret_t ret; char *name; char *host; /* * First try to find the kpasswd server, after all we are about to * try to change our password. If this fails then try admin_server. */ if (ret = kadm5_get_kpasswd(context, realm, &host)) { if (ret = kadm5_get_master(context, realm, &host)) return (ret); } name = malloc(strlen(KADM5_CHANGEPW_HOST_SERVICE) + strlen(host) + 2); if (name == NULL) { free(host); return (ENOMEM); } sprintf(name, "%s@%s", KADM5_CHANGEPW_HOST_SERVICE, host); free(host); *host_service_name = name; return (KADM5_OK); } /* * Get the host base service name for the kiprop principal. Returns * KADM5_OK on success. Caller must free the storage allocated * for host_service_name. */ kadm5_ret_t kadm5_get_kiprop_host_srv_name(krb5_context context, const char *realm, char **host_service_name) { kadm5_ret_t ret; char *name; char *host; if (ret = kadm5_get_master(context, realm, &host)) return (ret); name = malloc(strlen(KADM5_KIPROP_HOST_SERVICE) + strlen(host) + 2); if (name == NULL) { free(host); return (ENOMEM); } sprintf(name, "%s@%s", KADM5_KIPROP_HOST_SERVICE, host); free(host); *host_service_name = name; return (KADM5_OK); } /* * Solaris Kerberos: * Try to determine if this is the master KDC for a given realm */ kadm5_ret_t kadm5_is_master(krb5_context context, const char *realm, krb5_boolean *is_master) { kadm5_ret_t ret; char *admin_host = NULL; krb5_address **tmp_addr, **master_addr = NULL; krb5_address **local_addr = NULL; if (is_master) *is_master = FALSE; else return (KADM5_FAILURE); /* Locate the master KDC */ if (ret = kadm5_get_master(context, realm, &admin_host)) return (ret); if (ret = krb5_os_hostaddr(context, admin_host, &master_addr)) { free(admin_host); return (ret); } /* Get the local addresses */ if (ret = krb5_os_localaddr(context, &local_addr)) { krb5_free_addresses(context, master_addr); free(admin_host); return (ret); } /* Compare them */ for (tmp_addr = master_addr; *tmp_addr; tmp_addr++) { if (krb5_address_search(context, *tmp_addr, local_addr)) { *is_master = TRUE; break; } } krb5_free_addresses(context, local_addr); krb5_free_addresses(context, master_addr); free(admin_host); return (KADM5_OK); } /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ #ifndef __KADM_RPC_H__ #define __KADM_RPC_H__ #include #include #include struct cprinc_arg { krb5_ui_4 api_version; kadm5_principal_ent_rec rec; long mask; char *passwd; }; typedef struct cprinc_arg cprinc_arg; struct cprinc3_arg { krb5_ui_4 api_version; kadm5_principal_ent_rec rec; long mask; int n_ks_tuple; krb5_key_salt_tuple *ks_tuple; char *passwd; }; typedef struct cprinc3_arg cprinc3_arg; struct generic_ret { krb5_ui_4 api_version; kadm5_ret_t code; }; typedef struct generic_ret generic_ret; struct dprinc_arg { krb5_ui_4 api_version; krb5_principal princ; }; typedef struct dprinc_arg dprinc_arg; struct mprinc_arg { krb5_ui_4 api_version; kadm5_principal_ent_rec rec; long mask; }; typedef struct mprinc_arg mprinc_arg; struct rprinc_arg { krb5_ui_4 api_version; krb5_principal src; krb5_principal dest; }; typedef struct rprinc_arg rprinc_arg; struct gprincs_arg { krb5_ui_4 api_version; char *exp; }; typedef struct gprincs_arg gprincs_arg; struct gprincs_ret { krb5_ui_4 api_version; kadm5_ret_t code; char **princs; int count; }; typedef struct gprincs_ret gprincs_ret; struct chpass_arg { krb5_ui_4 api_version; krb5_principal princ; char *pass; }; typedef struct chpass_arg chpass_arg; struct chpass3_arg { krb5_ui_4 api_version; krb5_principal princ; krb5_boolean keepold; int n_ks_tuple; krb5_key_salt_tuple *ks_tuple; char *pass; }; typedef struct chpass3_arg chpass3_arg; struct setv4key_arg { krb5_ui_4 api_version; krb5_principal princ; krb5_keyblock *keyblock; }; typedef struct setv4key_arg setv4key_arg; struct setkey_arg { krb5_ui_4 api_version; krb5_principal princ; krb5_keyblock *keyblocks; int n_keys; }; typedef struct setkey_arg setkey_arg; struct setkey3_arg { krb5_ui_4 api_version; krb5_principal princ; krb5_boolean keepold; int n_ks_tuple; krb5_key_salt_tuple *ks_tuple; krb5_keyblock *keyblocks; int n_keys; }; typedef struct setkey3_arg setkey3_arg; struct chrand_arg { krb5_ui_4 api_version; krb5_principal princ; }; typedef struct chrand_arg chrand_arg; struct chrand3_arg { krb5_ui_4 api_version; krb5_principal princ; krb5_boolean keepold; int n_ks_tuple; krb5_key_salt_tuple *ks_tuple; }; typedef struct chrand3_arg chrand3_arg; struct chrand_ret { krb5_ui_4 api_version; kadm5_ret_t code; krb5_keyblock key; krb5_keyblock *keys; int n_keys; }; typedef struct chrand_ret chrand_ret; struct gprinc_arg { krb5_ui_4 api_version; krb5_principal princ; long mask; }; typedef struct gprinc_arg gprinc_arg; struct gprinc_ret { krb5_ui_4 api_version; kadm5_ret_t code; kadm5_principal_ent_rec rec; }; typedef struct gprinc_ret gprinc_ret; struct cpol_arg { krb5_ui_4 api_version; kadm5_policy_ent_rec rec; long mask; }; typedef struct cpol_arg cpol_arg; struct dpol_arg { krb5_ui_4 api_version; char *name; }; typedef struct dpol_arg dpol_arg; struct mpol_arg { krb5_ui_4 api_version; kadm5_policy_ent_rec rec; long mask; }; typedef struct mpol_arg mpol_arg; struct gpol_arg { krb5_ui_4 api_version; char *name; }; typedef struct gpol_arg gpol_arg; struct gpol_ret { krb5_ui_4 api_version; kadm5_ret_t code; kadm5_policy_ent_rec rec; }; typedef struct gpol_ret gpol_ret; struct gpols_arg { krb5_ui_4 api_version; char *exp; }; typedef struct gpols_arg gpols_arg; struct gpols_ret { krb5_ui_4 api_version; kadm5_ret_t code; char **pols; int count; }; typedef struct gpols_ret gpols_ret; struct getprivs_ret { krb5_ui_4 api_version; kadm5_ret_t code; long privs; }; typedef struct getprivs_ret getprivs_ret; #define KADM 2112 #define KADMVERS 2 #define CREATE_PRINCIPAL 1 extern generic_ret * create_principal_2(cprinc_arg *, CLIENT *); extern generic_ret * create_principal_2_svc(cprinc_arg *, struct svc_req *); #define DELETE_PRINCIPAL 2 extern generic_ret * delete_principal_2(dprinc_arg *, CLIENT *); extern generic_ret * delete_principal_2_svc(dprinc_arg *, struct svc_req *); #define MODIFY_PRINCIPAL 3 extern generic_ret * modify_principal_2(mprinc_arg *, CLIENT *); extern generic_ret * modify_principal_2_svc(mprinc_arg *, struct svc_req *); #define RENAME_PRINCIPAL 4 extern generic_ret * rename_principal_2(rprinc_arg *, CLIENT *); extern generic_ret * rename_principal_2_svc(rprinc_arg *, struct svc_req *); #define GET_PRINCIPAL 5 extern gprinc_ret * get_principal_2(gprinc_arg *, CLIENT *); extern gprinc_ret * get_principal_2_svc(gprinc_arg *, struct svc_req *); #define CHPASS_PRINCIPAL 6 extern generic_ret * chpass_principal_2(chpass_arg *, CLIENT *); extern generic_ret * chpass_principal_2_svc(chpass_arg *, struct svc_req *); #define CHRAND_PRINCIPAL 7 extern chrand_ret * chrand_principal_2(chrand_arg *, CLIENT *); extern chrand_ret * chrand_principal_2_svc(chrand_arg *, struct svc_req *); #define CREATE_POLICY 8 extern generic_ret * create_policy_2(cpol_arg *, CLIENT *); extern generic_ret * create_policy_2_svc(cpol_arg *, struct svc_req *); #define DELETE_POLICY 9 extern generic_ret * delete_policy_2(dpol_arg *, CLIENT *); extern generic_ret * delete_policy_2_svc(dpol_arg *, struct svc_req *); #define MODIFY_POLICY 10 extern generic_ret * modify_policy_2(mpol_arg *, CLIENT *); extern generic_ret * modify_policy_2_svc(mpol_arg *, struct svc_req *); #define GET_POLICY 11 extern gpol_ret * get_policy_2(gpol_arg *, CLIENT *); extern gpol_ret * get_policy_2_svc(gpol_arg *, struct svc_req *); #define GET_PRIVS 12 extern getprivs_ret * get_privs_2(void *, CLIENT *); extern getprivs_ret * get_privs_2_svc(krb5_ui_4 *, struct svc_req *); #define INIT 13 extern generic_ret * init_2(void *, CLIENT *); extern generic_ret * init_2_svc(krb5_ui_4 *, struct svc_req *); #define GET_PRINCS 14 extern gprincs_ret * get_princs_2(gprincs_arg *, CLIENT *); extern gprincs_ret * get_princs_2_svc(gprincs_arg *, struct svc_req *); #define GET_POLS 15 extern gpols_ret * get_pols_2(gpols_arg *, CLIENT *); extern gpols_ret * get_pols_2_svc(gpols_arg *, struct svc_req *); #define SETKEY_PRINCIPAL 16 extern generic_ret * setkey_principal_2(setkey_arg *, CLIENT *); extern generic_ret * setkey_principal_2_svc(setkey_arg *, struct svc_req *); #define SETV4KEY_PRINCIPAL 17 extern generic_ret * setv4key_principal_2(setv4key_arg *, CLIENT *); extern generic_ret * setv4key_principal_2_svc(setv4key_arg *, struct svc_req *); #define CREATE_PRINCIPAL3 18 extern generic_ret * create_principal3_2(cprinc3_arg *, CLIENT *); extern generic_ret * create_principal3_2_svc(cprinc3_arg *, struct svc_req *); #define CHPASS_PRINCIPAL3 19 extern generic_ret * chpass_principal3_2(chpass3_arg *, CLIENT *); extern generic_ret * chpass_principal3_2_svc(chpass3_arg *, struct svc_req *); #define CHRAND_PRINCIPAL3 20 extern chrand_ret * chrand_principal3_2(chrand3_arg *, CLIENT *); extern chrand_ret * chrand_principal3_2_svc(chrand3_arg *, struct svc_req *); #define SETKEY_PRINCIPAL3 21 extern generic_ret * setkey_principal3_2(setkey3_arg *, CLIENT *); extern generic_ret * setkey_principal3_2_svc(setkey3_arg *, struct svc_req *); extern bool_t xdr_cprinc_arg (); extern bool_t xdr_cprinc3_arg (); extern bool_t xdr_generic_ret (); extern bool_t xdr_dprinc_arg (); extern bool_t xdr_mprinc_arg (); extern bool_t xdr_rprinc_arg (); extern bool_t xdr_gprincs_arg (); extern bool_t xdr_gprincs_ret (); extern bool_t xdr_chpass_arg (); extern bool_t xdr_chpass3_arg (); extern bool_t xdr_setv4key_arg (); extern bool_t xdr_setkey_arg (); extern bool_t xdr_setkey3_arg (); extern bool_t xdr_chrand_arg (); extern bool_t xdr_chrand3_arg (); extern bool_t xdr_chrand_ret (); extern bool_t xdr_gprinc_arg (); extern bool_t xdr_gprinc_ret (); extern bool_t xdr_kadm5_ret_t (); extern bool_t xdr_kadm5_principal_ent_rec (); extern bool_t xdr_kadm5_policy_ent_rec (); extern bool_t xdr_krb5_keyblock (); extern bool_t xdr_krb5_principal (); extern bool_t xdr_krb5_enctype (); extern bool_t xdr_krb5_octet (); extern bool_t xdr_krb5_int32 (); extern bool_t xdr_u_int32 (); extern bool_t xdr_cpol_arg (); extern bool_t xdr_dpol_arg (); extern bool_t xdr_mpol_arg (); extern bool_t xdr_gpol_arg (); extern bool_t xdr_gpol_ret (); extern bool_t xdr_gpols_arg (); extern bool_t xdr_gpols_ret (); extern bool_t xdr_getprivs_ret (); #endif /* __KADM_RPC_H__ */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved */ #include #include #include #include #include #include #include static bool_t _xdr_kadm5_principal_ent_rec(XDR *xdrs, kadm5_principal_ent_rec *objp, int v); bool_t xdr_krb5_salttype(XDR *xdrs, krb5_int32 *objp); /* SUNWresync121 XXX */ /* * Function: xdr_ui_4 * * Purpose: XDR function which serves as a wrapper for xdr_u_int, * to prevent compiler warnings about type clashes between u_int32 * and krb5_ui_4. */ bool_t xdr_ui_4(XDR *xdrs, krb5_ui_4 *objp) { /* Assumes that krb5_ui_4 and u_int32 are both four bytes long. This should not be a harmful assumption. */ return xdr_u_int(xdrs, (uint32_t *) objp); } /* * Function: xdr_nullstring * * Purpose: XDR function for "strings" that are either NULL-terminated * or NULL. */ bool_t xdr_nullstring(XDR *xdrs, char **objp) { u_int size; if (xdrs->x_op == XDR_ENCODE) { if (*objp == NULL) size = 0; else size = strlen(*objp) + 1; } if (! xdr_u_int(xdrs, &size)) { return FALSE; } switch (xdrs->x_op) { case XDR_DECODE: if (size == 0) { *objp = NULL; return TRUE; } else if (*objp == NULL) { *objp = (char *) mem_alloc(size); if (*objp == NULL) { errno = ENOMEM; return FALSE; } } return (xdr_opaque(xdrs, *objp, size)); case XDR_ENCODE: if (size != 0) return (xdr_opaque(xdrs, *objp, size)); return TRUE; case XDR_FREE: if (*objp != NULL) mem_free(*objp, size); *objp = NULL; return TRUE; } return FALSE; } /* * Function: xdr_nulltype * * Purpose: XDR function for arbitrary pointer types that are either * NULL or contain data. */ bool_t xdr_nulltype(XDR *xdrs, void **objp, xdrproc_t proc) { bool_t null; switch (xdrs->x_op) { case XDR_DECODE: if (!xdr_bool(xdrs, &null)) return FALSE; if (null) { *objp = NULL; return TRUE; } return (*proc)(xdrs, objp); case XDR_ENCODE: if (*objp == NULL) null = TRUE; else null = FALSE; if (!xdr_bool(xdrs, &null)) return FALSE; if (null == FALSE) return (*proc)(xdrs, objp); return TRUE; case XDR_FREE: if (*objp) return (*proc)(xdrs, objp); return TRUE; } return FALSE; } bool_t xdr_krb5_timestamp(XDR *xdrs, krb5_timestamp *objp) { /* This assumes that int32 and krb5_timestamp are the same size. This shouldn't be a problem, since we've got a unit test which checks for this. */ if (!xdr_int(xdrs, (int32_t *) objp)) { return (FALSE); } return (TRUE); } bool_t xdr_krb5_kvno(XDR *xdrs, krb5_kvno *objp) { unsigned char tmp; tmp = '\0'; /* for purify, else xdr_u_char performs a umr */ if (xdrs->x_op == XDR_ENCODE) tmp = (unsigned char) *objp; if (!xdr_u_char(xdrs, &tmp)) return (FALSE); if (xdrs->x_op == XDR_DECODE) *objp = (krb5_kvno) tmp; return (TRUE); } bool_t xdr_krb5_deltat(XDR *xdrs, krb5_deltat *objp) { /* This assumes that int32 and krb5_deltat are the same size. This shouldn't be a problem, since we've got a unit test which checks for this. */ if (!xdr_int(xdrs, (int32_t *) objp)) { return (FALSE); } return (TRUE); } bool_t xdr_krb5_flags(XDR *xdrs, krb5_flags *objp) { /* This assumes that int32 and krb5_flags are the same size. This shouldn't be a problem, since we've got a unit test which checks for this. */ if (!xdr_int(xdrs, (int32_t *) objp)) { return (FALSE); } return (TRUE); } bool_t xdr_krb5_ui_4(XDR *xdrs, krb5_ui_4 *objp) { if (!xdr_u_int(xdrs, (uint32_t *) objp)) { return (FALSE); } return (TRUE); } bool_t xdr_krb5_int16(XDR *xdrs, krb5_int16 *objp) { int tmp; tmp = (int) *objp; if (!xdr_int(xdrs, &tmp)) return(FALSE); *objp = (krb5_int16) tmp; return(TRUE); } /* * Function: xdr_krb5_ui_2 * * Purpose: XDR function which serves as a wrapper for xdr_u_int, * to prevent compiler warnings about type clashes between u_int * and krb5_ui_2. */ bool_t xdr_krb5_ui_2(XDR *xdrs, krb5_ui_2 *objp) { unsigned int tmp; tmp = (unsigned int) *objp; if (!xdr_u_int(xdrs, &tmp)) return(FALSE); *objp = (krb5_ui_2) tmp; return(TRUE); } bool_t xdr_krb5_key_data_nocontents(XDR *xdrs, krb5_key_data *objp) { /* * Note that this function intentionally DOES NOT tranfer key * length or contents! xdr_krb5_key_data in adb_xdr.c does, but * that is only for use within the server-side library. */ unsigned int tmp; if (xdrs->x_op == XDR_DECODE) memset((char *) objp, 0, sizeof(krb5_key_data)); if (!xdr_krb5_int16(xdrs, &objp->key_data_ver)) { return (FALSE); } if (!xdr_krb5_int16(xdrs, &objp->key_data_kvno)) { return (FALSE); } if (!xdr_krb5_int16(xdrs, &objp->key_data_type[0])) { return (FALSE); } if (objp->key_data_ver > 1) { if (!xdr_krb5_int16(xdrs, &objp->key_data_type[1])) { return (FALSE); } } /* * kadm5_get_principal on the server side allocates and returns * key contents when asked. Even though this function refuses to * transmit that data, it still has to *free* the data at the * appropriate time to avoid a memory leak. */ if (xdrs->x_op == XDR_FREE) { tmp = (unsigned int) objp->key_data_length[0]; if (!xdr_bytes(xdrs, (char **) &objp->key_data_contents[0], &tmp, ~0)) return FALSE; tmp = (unsigned int) objp->key_data_length[1]; if (!xdr_bytes(xdrs, (char **) &objp->key_data_contents[1], &tmp, ~0)) return FALSE; } return (TRUE); } bool_t xdr_krb5_key_salt_tuple(XDR *xdrs, krb5_key_salt_tuple *objp) { if (!xdr_krb5_enctype(xdrs, &objp->ks_enctype)) return FALSE; if (!xdr_krb5_salttype(xdrs, &objp->ks_salttype)) return FALSE; return TRUE; } bool_t xdr_krb5_tl_data(XDR *xdrs, krb5_tl_data **tl_data_head) { krb5_tl_data *tl, *tl2; bool_t more; unsigned int len; switch (xdrs->x_op) { case XDR_FREE: tl = tl2 = *tl_data_head; while (tl) { tl2 = tl->tl_data_next; free(tl->tl_data_contents); free(tl); tl = tl2; } break; case XDR_ENCODE: tl = *tl_data_head; while (1) { more = (tl != NULL); if (!xdr_bool(xdrs, &more)) return FALSE; if (tl == NULL) break; if (!xdr_krb5_int16(xdrs, &tl->tl_data_type)) return FALSE; len = tl->tl_data_length; if (!xdr_bytes(xdrs, (char **) &tl->tl_data_contents, &len, ~0)) return FALSE; tl = tl->tl_data_next; } break; case XDR_DECODE: tl = NULL; while (1) { if (!xdr_bool(xdrs, &more)) return FALSE; if (more == FALSE) break; tl2 = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)); if (tl2 == NULL) return FALSE; memset((char *) tl2, 0, sizeof(krb5_tl_data)); if (!xdr_krb5_int16(xdrs, &tl2->tl_data_type)) return FALSE; if (!xdr_bytes(xdrs, (char **)&tl2->tl_data_contents, &len, ~0)) return FALSE; tl2->tl_data_length = len; tl2->tl_data_next = tl; tl = tl2; } *tl_data_head = tl; break; } return TRUE; } bool_t xdr_kadm5_ret_t(XDR *xdrs, kadm5_ret_t *objp) { uint32_t tmp; if (xdrs->x_op == XDR_ENCODE) tmp = (uint32_t) *objp; if (!xdr_u_int(xdrs, &tmp)) return (FALSE); if (xdrs->x_op == XDR_DECODE) *objp = (kadm5_ret_t) tmp; return (TRUE); } bool_t xdr_kadm5_principal_ent_rec_v1(XDR *xdrs, kadm5_principal_ent_rec *objp) { return _xdr_kadm5_principal_ent_rec(xdrs, objp, KADM5_API_VERSION_1); } bool_t xdr_kadm5_principal_ent_rec(XDR *xdrs, kadm5_principal_ent_rec *objp) { return _xdr_kadm5_principal_ent_rec(xdrs, objp, KADM5_API_VERSION_2); } static bool_t _xdr_kadm5_principal_ent_rec(XDR *xdrs, kadm5_principal_ent_rec *objp, int v) { unsigned int n; if (!xdr_krb5_principal(xdrs, &objp->principal)) { return (FALSE); } if (!xdr_krb5_timestamp(xdrs, &objp->princ_expire_time)) { return (FALSE); } if (!xdr_krb5_timestamp(xdrs, &objp->last_pwd_change)) { return (FALSE); } if (!xdr_krb5_timestamp(xdrs, &objp->pw_expiration)) { return (FALSE); } if (!xdr_krb5_deltat(xdrs, &objp->max_life)) { return (FALSE); } if (v == KADM5_API_VERSION_1) { if (!xdr_krb5_principal(xdrs, &objp->mod_name)) { return (FALSE); } } else { if (!xdr_nulltype(xdrs, (void **) &objp->mod_name, xdr_krb5_principal)) { return (FALSE); } } if (!xdr_krb5_timestamp(xdrs, &objp->mod_date)) { return (FALSE); } if (!xdr_krb5_flags(xdrs, &objp->attributes)) { return (FALSE); } if (!xdr_krb5_kvno(xdrs, &objp->kvno)) { return (FALSE); } if (!xdr_krb5_kvno(xdrs, &objp->mkvno)) { return (FALSE); } if (!xdr_nullstring(xdrs, &objp->policy)) { return (FALSE); } if (!xdr_long(xdrs, &objp->aux_attributes)) { return (FALSE); } if (v != KADM5_API_VERSION_1) { if (!xdr_krb5_deltat(xdrs, &objp->max_renewable_life)) { return (FALSE); } if (!xdr_krb5_timestamp(xdrs, &objp->last_success)) { return (FALSE); } if (!xdr_krb5_timestamp(xdrs, &objp->last_failed)) { return (FALSE); } if (!xdr_krb5_kvno(xdrs, &objp->fail_auth_count)) { return (FALSE); } if (!xdr_krb5_int16(xdrs, &objp->n_key_data)) { return (FALSE); } if (!xdr_krb5_int16(xdrs, &objp->n_tl_data)) { return (FALSE); } if (!xdr_nulltype(xdrs, (void **) &objp->tl_data, xdr_krb5_tl_data)) { return FALSE; } n = objp->n_key_data; if (!xdr_array(xdrs, (caddr_t *) &objp->key_data, &n, ~0, sizeof(krb5_key_data), xdr_krb5_key_data_nocontents)) { return (FALSE); } } return (TRUE); } bool_t xdr_kadm5_policy_ent_rec(XDR *xdrs, kadm5_policy_ent_rec *objp) { if (!xdr_nullstring(xdrs, &objp->policy)) { return (FALSE); } /* these all used to be u_int32, but it's stupid for sized types to be exposed at the api, and they're the same as longs on the wire. */ if (!xdr_long(xdrs, &objp->pw_min_life)) { return (FALSE); } if (!xdr_long(xdrs, &objp->pw_max_life)) { return (FALSE); } if (!xdr_long(xdrs, &objp->pw_min_length)) { return (FALSE); } if (!xdr_long(xdrs, &objp->pw_min_classes)) { return (FALSE); } if (!xdr_long(xdrs, &objp->pw_history_num)) { return (FALSE); } if (!xdr_long(xdrs, &objp->policy_refcnt)) { return (FALSE); } return (TRUE); } bool_t xdr_cprinc_arg(XDR *xdrs, cprinc_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (objp->api_version == KADM5_API_VERSION_1) { if (!xdr_kadm5_principal_ent_rec_v1(xdrs, &objp->rec)) { return (FALSE); } } else { if (!xdr_kadm5_principal_ent_rec(xdrs, &objp->rec)) { return (FALSE); } } if (!xdr_long(xdrs, &objp->mask)) { return (FALSE); } if (!xdr_nullstring(xdrs, &objp->passwd)) { return (FALSE); } return (TRUE); } bool_t xdr_cprinc3_arg(XDR *xdrs, cprinc3_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (objp->api_version == KADM5_API_VERSION_1) { if (!xdr_kadm5_principal_ent_rec_v1(xdrs, &objp->rec)) { return (FALSE); } } else { if (!xdr_kadm5_principal_ent_rec(xdrs, &objp->rec)) { return (FALSE); } } if (!xdr_long(xdrs, &objp->mask)) { return (FALSE); } if (!xdr_array(xdrs, (caddr_t *)&objp->ks_tuple, (unsigned int *)&objp->n_ks_tuple, ~0, sizeof(krb5_key_salt_tuple), xdr_krb5_key_salt_tuple)) { return (FALSE); } if (!xdr_nullstring(xdrs, &objp->passwd)) { return (FALSE); } return (TRUE); } bool_t xdr_generic_ret(XDR *xdrs, generic_ret *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_kadm5_ret_t(xdrs, &objp->code)) { return (FALSE); } return(TRUE); } bool_t xdr_dprinc_arg(XDR *xdrs, dprinc_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_krb5_principal(xdrs, &objp->princ)) { return (FALSE); } return (TRUE); } bool_t xdr_mprinc_arg(XDR *xdrs, mprinc_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (objp->api_version == KADM5_API_VERSION_1) { if (!xdr_kadm5_principal_ent_rec_v1(xdrs, &objp->rec)) { return (FALSE); } } else { if (!xdr_kadm5_principal_ent_rec(xdrs, &objp->rec)) { return (FALSE); } } if (!xdr_long(xdrs, &objp->mask)) { return (FALSE); } return (TRUE); } bool_t xdr_rprinc_arg(XDR *xdrs, rprinc_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_krb5_principal(xdrs, &objp->src)) { return (FALSE); } if (!xdr_krb5_principal(xdrs, &objp->dest)) { return (FALSE); } return (TRUE); } bool_t xdr_gprincs_arg(XDR *xdrs, gprincs_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_nullstring(xdrs, &objp->exp)) { return (FALSE); } return (TRUE); } bool_t xdr_gprincs_ret(XDR *xdrs, gprincs_ret *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_kadm5_ret_t(xdrs, &objp->code)) { return (FALSE); } if (objp->code == KADM5_OK) { if (!xdr_int(xdrs, &objp->count)) { return (FALSE); } if (!xdr_array(xdrs, (caddr_t *) &objp->princs, (unsigned int *) &objp->count, ~0, sizeof(char *), xdr_nullstring)) { return (FALSE); } } return (TRUE); } bool_t xdr_chpass_arg(XDR *xdrs, chpass_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_krb5_principal(xdrs, &objp->princ)) { return (FALSE); } if (!xdr_nullstring(xdrs, &objp->pass)) { return (FALSE); } return (TRUE); } bool_t xdr_chpass3_arg(XDR *xdrs, chpass3_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_krb5_principal(xdrs, &objp->princ)) { return (FALSE); } if (!xdr_bool(xdrs, (bool_t *) &objp->keepold)) { /* SUNWresync121 XXX */ return (FALSE); } if (!xdr_array(xdrs, (caddr_t *)&objp->ks_tuple, (unsigned int*)&objp->n_ks_tuple, ~0, sizeof(krb5_key_salt_tuple), xdr_krb5_key_salt_tuple)) { return (FALSE); } if (!xdr_nullstring(xdrs, &objp->pass)) { return (FALSE); } return (TRUE); } bool_t xdr_setv4key_arg(XDR *xdrs, setv4key_arg *objp) { unsigned int n_keys = 1; if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_krb5_principal(xdrs, &objp->princ)) { return (FALSE); } if (!xdr_array(xdrs, (caddr_t *) &objp->keyblock, &n_keys, ~0, sizeof(krb5_keyblock), xdr_krb5_keyblock)) { return (FALSE); } return (TRUE); } bool_t xdr_setkey_arg(XDR *xdrs, setkey_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_krb5_principal(xdrs, &objp->princ)) { return (FALSE); } if (!xdr_array(xdrs, (caddr_t *) &objp->keyblocks, (unsigned int *) &objp->n_keys, ~0, sizeof(krb5_keyblock), xdr_krb5_keyblock)) { return (FALSE); } return (TRUE); } bool_t xdr_setkey3_arg(XDR *xdrs, setkey3_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_krb5_principal(xdrs, &objp->princ)) { return (FALSE); } if (!xdr_bool(xdrs, (bool_t *) &objp->keepold)) { /* SUNWresync121 XXX */ return (FALSE); } if (!xdr_array(xdrs, (caddr_t *) &objp->ks_tuple, (unsigned int *) &objp->n_ks_tuple, ~0, sizeof(krb5_key_salt_tuple), xdr_krb5_key_salt_tuple)) { return (FALSE); } if (!xdr_array(xdrs, (caddr_t *) &objp->keyblocks, (unsigned int *) &objp->n_keys, ~0, sizeof(krb5_keyblock), xdr_krb5_keyblock)) { return (FALSE); } return (TRUE); } bool_t xdr_chrand_arg(XDR *xdrs, chrand_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_krb5_principal(xdrs, &objp->princ)) { return (FALSE); } return (TRUE); } bool_t xdr_chrand3_arg(XDR *xdrs, chrand3_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_krb5_principal(xdrs, &objp->princ)) { return (FALSE); } if (!xdr_bool(xdrs, (bool_t *) &objp->keepold)) { /* SUNWresync121 XXX */ return (FALSE); } if (!xdr_array(xdrs, (caddr_t *)&objp->ks_tuple, (unsigned int*)&objp->n_ks_tuple, ~0, sizeof(krb5_key_salt_tuple), xdr_krb5_key_salt_tuple)) { return (FALSE); } return (TRUE); } bool_t xdr_chrand_ret(XDR *xdrs, chrand_ret *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_kadm5_ret_t(xdrs, &objp->code)) { return (FALSE); } if (objp->api_version == KADM5_API_VERSION_1) { if(objp->code == KADM5_OK) { if (!xdr_krb5_keyblock(xdrs, &objp->key)) { return (FALSE); } } } else { if (objp->code == KADM5_OK) { if (!xdr_array(xdrs, (char **)&objp->keys, (unsigned int *)&objp->n_keys, ~0, sizeof(krb5_keyblock), xdr_krb5_keyblock)) return FALSE; } } return (TRUE); } bool_t xdr_gprinc_arg(XDR *xdrs, gprinc_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_krb5_principal(xdrs, &objp->princ)) { return (FALSE); } if ((objp->api_version > KADM5_API_VERSION_1) && !xdr_long(xdrs, &objp->mask)) { return FALSE; } return (TRUE); } bool_t xdr_gprinc_ret(XDR *xdrs, gprinc_ret *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_kadm5_ret_t(xdrs, &objp->code)) { return (FALSE); } if(objp->code == KADM5_OK) { if (objp->api_version == KADM5_API_VERSION_1) { if (!xdr_kadm5_principal_ent_rec_v1(xdrs, &objp->rec)) { return (FALSE); } } else { if (!xdr_kadm5_principal_ent_rec(xdrs, &objp->rec)) { return (FALSE); } } } return (TRUE); } bool_t xdr_cpol_arg(XDR *xdrs, cpol_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_kadm5_policy_ent_rec(xdrs, &objp->rec)) { return (FALSE); } if (!xdr_long(xdrs, &objp->mask)) { return (FALSE); } return (TRUE); } bool_t xdr_dpol_arg(XDR *xdrs, dpol_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_nullstring(xdrs, &objp->name)) { return (FALSE); } return (TRUE); } bool_t xdr_mpol_arg(XDR *xdrs, mpol_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_kadm5_policy_ent_rec(xdrs, &objp->rec)) { return (FALSE); } if (!xdr_long(xdrs, &objp->mask)) { return (FALSE); } return (TRUE); } bool_t xdr_gpol_arg(XDR *xdrs, gpol_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_nullstring(xdrs, &objp->name)) { return (FALSE); } return (TRUE); } bool_t xdr_gpol_ret(XDR *xdrs, gpol_ret *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_kadm5_ret_t(xdrs, &objp->code)) { return (FALSE); } if(objp->code == KADM5_OK) { if (!xdr_kadm5_policy_ent_rec(xdrs, &objp->rec)) return (FALSE); } return (TRUE); } bool_t xdr_gpols_arg(XDR *xdrs, gpols_arg *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_nullstring(xdrs, &objp->exp)) { return (FALSE); } return (TRUE); } bool_t xdr_gpols_ret(XDR *xdrs, gpols_ret *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (!xdr_kadm5_ret_t(xdrs, &objp->code)) { return (FALSE); } if (objp->code == KADM5_OK) { if (!xdr_int(xdrs, &objp->count)) { return (FALSE); } if (!xdr_array(xdrs, (caddr_t *) &objp->pols, (unsigned int *) &objp->count, ~0, sizeof(char *), xdr_nullstring)) { return (FALSE); } } return (TRUE); } bool_t xdr_getprivs_ret(XDR *xdrs, getprivs_ret *objp) { if (!xdr_ui_4(xdrs, &objp->api_version)) { return (FALSE); } if (! xdr_kadm5_ret_t(xdrs, &objp->code) || ! xdr_long(xdrs, &objp->privs)) return FALSE; return TRUE; } bool_t xdr_krb5_principal(XDR *xdrs, krb5_principal *objp) { int ret; char *p = NULL; krb5_principal pr = NULL; static krb5_context context = NULL; /* using a static context here is ugly, but should work ok, and the other solutions are even uglier */ if (!context && kadm5_init_krb5_context(&context)) return(FALSE); switch(xdrs->x_op) { case XDR_ENCODE: if (*objp) { if((ret = krb5_unparse_name(context, *objp, &p)) != 0) return FALSE; } if(!xdr_nullstring(xdrs, &p)) return FALSE; if (p) free(p); break; case XDR_DECODE: if(!xdr_nullstring(xdrs, &p)) return FALSE; if (p) { ret = krb5_parse_name(context, p, &pr); if(ret != 0) return FALSE; *objp = pr; free(p); } else *objp = NULL; break; case XDR_FREE: if(*objp != NULL) krb5_free_principal(context, *objp); break; } return TRUE; } bool_t xdr_krb5_octet(XDR *xdrs, krb5_octet *objp) { if (!xdr_u_char(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_krb5_enctype(XDR *xdrs, krb5_enctype *objp) { /* * This used to be xdr_krb5_keytype, but keytypes and enctypes have * been merged into only enctypes. However, randkey_principal * already ensures that only a key of ENCTYPE_DES_CBC_CRC will be * returned to v1 clients, and ENCTYPE_DES_CBC_CRC has the same * value as KEYTYPE_DES used too, which is what all v1 clients * expect. Therefore, IMHO, just encoding whatever enctype we get * is safe. */ if (!xdr_u_int(xdrs, (unsigned int *) objp)) return (FALSE); return (TRUE); } bool_t xdr_krb5_salttype(XDR *xdrs, krb5_int32 *objp) { if (!xdr_int(xdrs, (int32_t *) objp)) return FALSE; return TRUE; } bool_t xdr_krb5_keyblock(XDR *xdrs, krb5_keyblock *objp) { /* XXX This only works because free_keyblock assumes ->contents is allocated by malloc() */ if(!xdr_krb5_enctype(xdrs, &objp->enctype)) return FALSE; if(!xdr_bytes(xdrs, (char **) &objp->contents, (unsigned int *) &objp->length, ~0)) return FALSE; return TRUE; } /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; #endif #include "server_internal.h" #include #include kadm5_ret_t kadm5_free_policy_ent(void *server_handle, kadm5_policy_ent_t val) { kadm5_server_handle_t handle = server_handle; _KADM5_CHECK_HANDLE(server_handle); if(val) { if (val->policy) free(val->policy); if (handle->api_version == KADM5_API_VERSION_1) free(val); } return KADM5_OK; } kadm5_ret_t kadm5_free_name_list(void *server_handle, char **names, int count) { _KADM5_CHECK_HANDLE(server_handle); while (count--) free(names[count]); free(names); return KADM5_OK; } /* XXX this ought to be in libkrb5.a, but isn't */ kadm5_ret_t krb5_free_key_data_contents(context, key) krb5_context context; krb5_key_data *key; { int i, idx; idx = (key->key_data_ver == 1 ? 1 : 2); for (i = 0; i < idx; i++) { if (key->key_data_contents[i]) { memset(key->key_data_contents[i], 0, key->key_data_length[i]); free(key->key_data_contents[i]); } } return KADM5_OK; } kadm5_ret_t kadm5_free_key_data(void *server_handle, krb5_int16 *n_key_data, krb5_key_data *key_data) { kadm5_server_handle_t handle = server_handle; int i, nkeys = (int) *n_key_data; _KADM5_CHECK_HANDLE(server_handle); if (key_data == NULL) return KADM5_OK; for (i = 0; i < nkeys; i++) krb5_free_key_data_contents(handle->context, &key_data[i]); free(key_data); return KADM5_OK; } kadm5_ret_t kadm5_free_principal_ent(void *server_handle, kadm5_principal_ent_t val) { kadm5_server_handle_t handle = server_handle; int i; _KADM5_CHECK_HANDLE(server_handle); if(val) { if(val->principal) krb5_free_principal(handle->context, val->principal); if(val->mod_name) krb5_free_principal(handle->context, val->mod_name); if(val->policy) free(val->policy); if (handle->api_version > KADM5_API_VERSION_1) { if (val->n_key_data) { for (i = 0; i < val->n_key_data; i++) krb5_free_key_data_contents(handle->context, &val->key_data[i]); free(val->key_data); } if (val->tl_data) { krb5_tl_data *tl; while (val->tl_data) { tl = val->tl_data->tl_data_next; free(val->tl_data->tl_data_contents); free(val->tl_data); val->tl_data = tl; } } } if (handle->api_version == KADM5_API_VERSION_1) free(val); } return KADM5_OK; } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ */ /* * This header file is used internally by the Admin API server * libraries and Admin server. IF YOU THINK YOU NEED TO USE THIS FILE * FOR ANYTHING, YOU'RE ALMOST CERTAINLY WRONG. */ #ifndef __KADM5_SERVER_INTERNAL_H__ #define __KADM5_SERVER_INTERNAL_H__ #ifdef HAVE_MEMORY_H #include #endif #include #include #include "k5-int.h" #include #include #include #include "admin_internal.h" typedef struct _kadm5_server_handle_t { krb5_ui_4 magic_number; krb5_ui_4 struct_version; krb5_ui_4 api_version; krb5_context context; krb5_principal current_caller; kadm5_config_params params; struct _kadm5_server_handle_t *lhandle; char **db_args; krb5_keyblock master_keyblock; } kadm5_server_handle_rec, *kadm5_server_handle_t; #define OSA_ADB_PRINC_VERSION_1 0x12345C01 typedef struct _osa_pw_hist_t { int n_key_data; krb5_key_data *key_data; } osa_pw_hist_ent, *osa_pw_hist_t; typedef struct _osa_princ_ent_t { int version; char *policy; long aux_attributes; unsigned int old_key_len; unsigned int old_key_next; krb5_kvno admin_history_kvno; osa_pw_hist_ent *old_keys; } osa_princ_ent_rec, *osa_princ_ent_t; kadm5_ret_t adb_policy_init(kadm5_server_handle_t handle); kadm5_ret_t adb_policy_close(kadm5_server_handle_t handle); kadm5_ret_t passwd_check(kadm5_server_handle_t handle, char *pass, int use_policy, kadm5_policy_ent_t policy, krb5_principal principal); kadm5_ret_t principal_exists(krb5_principal principal); krb5_error_code kdb_init_master(kadm5_server_handle_t handle, char *r, int from_keyboard); krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r); krb5_error_code kdb_get_entry(kadm5_server_handle_t handle, krb5_principal principal, krb5_db_entry *kdb, osa_princ_ent_rec *adb); krb5_error_code kdb_free_entry(kadm5_server_handle_t handle, krb5_db_entry *kdb, osa_princ_ent_rec *adb); krb5_error_code kdb_put_entry(kadm5_server_handle_t handle, krb5_db_entry *kdb, osa_princ_ent_rec *adb); krb5_error_code kdb_delete_entry(kadm5_server_handle_t handle, krb5_principal name); krb5_error_code kdb_iter_entry(kadm5_server_handle_t handle, char *match_entry, void (*iter_fct)(void *, krb5_principal), void *data); int init_dict(kadm5_config_params *); int find_word(const char *word); void destroy_dict(void); /* XXX this ought to be in libkrb5.a, but isn't */ kadm5_ret_t krb5_copy_key_data_contents(krb5_context context, krb5_key_data *from, krb5_key_data *to); kadm5_ret_t krb5_free_key_data_contents(krb5_context context, krb5_key_data *key); /* * *Warning* * *Warning* This is going to break if we * *Warning* ever go multi-threaded * *Warning* */ extern krb5_principal current_caller; /* * Why is this (or something similar) not defined *anywhere* in krb5? */ #define KSUCCESS 0 #define WORD_NOT_FOUND 1 /* * all the various mask bits or'd together */ #define ALL_PRINC_MASK \ (KADM5_PRINCIPAL | KADM5_PRINC_EXPIRE_TIME | KADM5_PW_EXPIRATION | \ KADM5_LAST_PWD_CHANGE | KADM5_ATTRIBUTES | KADM5_MAX_LIFE | \ KADM5_MOD_TIME | KADM5_MOD_NAME | KADM5_KVNO | KADM5_MKVNO | \ KADM5_AUX_ATTRIBUTES | KADM5_POLICY_CLR | KADM5_POLICY | \ KADM5_MAX_RLIFE | KADM5_TL_DATA | KADM5_KEY_DATA) #define ALL_POLICY_MASK \ (KADM5_POLICY | KADM5_PW_MAX_LIFE | KADM5_PW_MIN_LIFE | \ KADM5_PW_MIN_LENGTH | KADM5_PW_MIN_CLASSES | KADM5_PW_HISTORY_NUM | \ KADM5_REF_COUNT) #define SERVER_CHECK_HANDLE(handle) \ { \ kadm5_server_handle_t srvr = \ (kadm5_server_handle_t) handle; \ \ if (! srvr->current_caller) \ return KADM5_BAD_SERVER_HANDLE; \ if (! srvr->lhandle) \ return KADM5_BAD_SERVER_HANDLE; \ } #define CHECK_HANDLE(handle) \ GENERIC_CHECK_HANDLE(handle, KADM5_OLD_SERVER_API_VERSION, \ KADM5_NEW_SERVER_API_VERSION) \ SERVER_CHECK_HANDLE(handle) bool_t xdr_osa_princ_ent_rec(XDR *xdrs, osa_princ_ent_t objp); void osa_free_princ_ent(osa_princ_ent_t val); #endif /* __KADM5_SERVER_INTERNAL_H__ */ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include ../../Makefile.lib # Hammerhead: amd64-only SUBDIRS= $(MACH64) all : TARGET= all clean : TARGET= clean clobber : TARGET= clobber install : TARGET= install _msg : TARGET= _msg .KEEP_STATE: all clean clobber install _msg: $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. LIBRARY= libkadm5srv.a VERS= .1 SRV_OBJS = svr_policy.o \ svr_principal.o \ server_acl.o \ server_kdb.o \ server_misc.o \ server_init.o \ server_dict.o \ svr_iters.o \ svr_chpass_util.o \ adb_xdr.o \ xdr_alloc.o \ logger.o \ chgpwd.o SHARED_OBJS = \ misc_free.o \ kadm_rpc_xdr.o \ chpass_util.o \ alt_prof.o \ kadm_host_srv_names.o \ str_conv.o OBJECTS= $(SHARED_OBJS) $(SRV_OBJS) # include library definitions include ../../../Makefile.lib SRCS= $(SRV_OBJS:%.o=../%.c) \ $(SHARED_OBJS:%.o=../../%.c) KRB5LIB= $(ROOT)/usr/lib/krb5 LIBS= $(DYNLIB) include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 POFILE = $(LIBRARY:%.a=%.po) POFILES = generic.po #override liblink INS.liblink= -$(RM) $@; $(SYMLINK) $(LIBLINKS)$(VERS) $@ CPPFLAGS += -I.. -I../.. -I../../.. \ -I$(SRC)/lib/krb5/kdb \ -I$(SRC)/cmd/krb5/iprop \ -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/lib/gss_mechs/mech_krb5/include/krb5 \ -I$(SRC)/uts/common/gssapi/ \ -I$(SRC)/uts/common/gssapi/include/ \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \ -I$(SRC)/lib/gss_mechs/mech_krb5/krb5/os \ -DHAVE_STDLIB_H -DUSE_SOLARIS_SHARED_LIBRARIES \ -DHAVE_LIBSOCKET=1 -DHAVE_LIBNSL=1 -DSETRPCENT_TYPE=void \ -DENDRPCENT_TYPE=void -DHAVE_SYS_ERRLIST=1 -DNEED_SYS_ERRLIST=1 \ -DHAVE_SYSLOG_H=1 -DHAVE_OPENLOG=1 -DHAVE_SYSLOG=1 -DHAVE_CLOSELOG=1 \ -DHAVE_STEP=1 -DHAVE_RE_COMP=1 -DHAVE_RE_EXEC=1 -DHAVE_REGCOMP=1 \ -DHAVE_REGEXEC=1 -DHAVE_STRFTIME=1 -DHAVE_VSPRINTF=1 \ -DUSE_KADM5_API_VERSION=2 # Hammerhead: -I.. already in CPPFLAGS above; removed from CFLAGS (lost in Makefile.master.64 reset) CFLAGS += $(CCVERBOSE) # Hammerhead: GNU ld with -zdefs requires all symbols resolved at link time. # Link against mech_krb5 for krb5_* symbols, plus standard system libs. # libkdb provides krb5_db_*, ulog_* symbols; libdyn provides Dyn* symbols. # Both live in $(ROOT)/usr/lib/krb5, so use full proto-area paths. LDLIBS += $(KMECHLIB) $(KRB5LIB)/libkdb.so $(KRB5LIB)/libdyn.so \ -lnsl -lsocket -lc CERRWARN += -Wno-unused-function CERRWARN += -Wno-unused-variable CERRWARN += -Wno-parentheses CERRWARN += -Wno-type-limits CERRWARN += $(CNOWARN_UNINIT) SMOFF += all_func_returns,indenting .KEEP_STATE: all: $(LIBS) # include library targets include ../../../Makefile.targ pics/%.o: ../../%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) FRC: generic.po: FRC $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext ../*.[ch] ../../*.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; #endif #include #include "server_internal.h" #include #include /* SUNWresync121 XXX */ #include "admin_xdr.h" #ifdef HAVE_MEMORY_H #include #endif bool_t xdr_krb5_key_data(XDR *xdrs, krb5_key_data *objp) { unsigned int tmp; if (!xdr_krb5_int16(xdrs, &objp->key_data_ver)) return(FALSE); if (!xdr_krb5_int16(xdrs, &objp->key_data_kvno)) return(FALSE); if (!xdr_krb5_int16(xdrs, &objp->key_data_type[0])) return(FALSE); if (!xdr_krb5_int16(xdrs, &objp->key_data_type[1])) return(FALSE); /* SUNW14resync */ if (!xdr_krb5_ui_2(xdrs, (krb5_ui_2 *)&objp->key_data_length[0])) return(FALSE); if (!xdr_krb5_ui_2(xdrs, (krb5_ui_2 *)&objp->key_data_length[1])) return(FALSE); tmp = (unsigned int) objp->key_data_length[0]; if (!xdr_bytes(xdrs, (char **) &objp->key_data_contents[0], &tmp, ~0)) return FALSE; tmp = (unsigned int) objp->key_data_length[1]; if (!xdr_bytes(xdrs, (char **) &objp->key_data_contents[1], &tmp, ~0)) return FALSE; /* don't need to copy tmp out, since key_data_length will be set by the above encoding. */ return(TRUE); } bool_t xdr_osa_pw_hist_ent(XDR *xdrs, osa_pw_hist_ent *objp) { if (!xdr_array(xdrs, (caddr_t *) &objp->key_data, (u_int *) &objp->n_key_data, ~0, sizeof(krb5_key_data), xdr_krb5_key_data)) return (FALSE); return (TRUE); } bool_t xdr_osa_princ_ent_rec(XDR *xdrs, osa_princ_ent_t objp) { switch (xdrs->x_op) { case XDR_ENCODE: objp->version = OSA_ADB_PRINC_VERSION_1; /* fall through */ case XDR_FREE: if (!xdr_int(xdrs, &objp->version)) return FALSE; break; case XDR_DECODE: if (!xdr_int(xdrs, &objp->version)) return FALSE; if (objp->version != OSA_ADB_PRINC_VERSION_1) return FALSE; break; } if (!xdr_nullstring(xdrs, &objp->policy)) return (FALSE); if (!xdr_long(xdrs, &objp->aux_attributes)) return (FALSE); if (!xdr_u_int(xdrs, &objp->old_key_next)) return (FALSE); if (!xdr_krb5_kvno(xdrs, &objp->admin_history_kvno)) return (FALSE); if (!xdr_array(xdrs, (caddr_t *) &objp->old_keys, (unsigned int *) &objp->old_key_len, ~0, sizeof(osa_pw_hist_ent), xdr_osa_pw_hist_ent)) return (FALSE); return (TRUE); } void osa_free_princ_ent(osa_princ_ent_t val) { XDR xdrs; xdrmem_create(&xdrs, NULL, 0, XDR_FREE); xdr_osa_princ_ent_rec(&xdrs, val); free(val); } # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2025 Hammerhead Project # include ../Makefile.com include $(SRC)/lib/Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * lib/krb5/kadm5/srv/chgpwd.c * * Copyright 1998 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ /* * chgpwd.c - Handles changepw requests issued from non-Solaris krb5 clients. */ #include #include #include #include #include #define MAXAPREQ 1500 static krb5_error_code process_chpw_request(krb5_context context, void *server_handle, char *realm, int s, krb5_keytab keytab, struct sockaddr_in *sin, krb5_data *req, krb5_data *rep) { krb5_error_code ret; char *ptr; int plen, vno; krb5_address local_kaddr, remote_kaddr; int allocated_mem = 0; krb5_data ap_req, ap_rep; krb5_auth_context auth_context; krb5_principal changepw; krb5_ticket *ticket; krb5_data cipher, clear; struct sockaddr local_addr, remote_addr; int addrlen; krb5_replay_data replay; krb5_error krberror; int numresult; char strresult[1024]; char *clientstr; size_t clen; char *cdots; ret = 0; rep->length = 0; auth_context = NULL; changepw = NULL; ap_rep.length = 0; ap_rep.data = NULL; ticket = NULL; clear.length = 0; clear.data = NULL; cipher.length = 0; cipher.data = NULL; if (req->length < 4) { /* * either this, or the server is printing bad messages, * or the caller passed in garbage */ ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; (void) strlcpy(strresult, "Request was truncated", sizeof (strresult)); goto chpwfail; } ptr = req->data; /* * Verify length */ plen = (*ptr++ & 0xff); plen = (plen<<8) | (*ptr++ & 0xff); if (plen != req->length) return (KRB5KRB_AP_ERR_MODIFIED); /* * Verify version number */ vno = (*ptr++ & 0xff); vno = (vno<<8) | (*ptr++ & 0xff); if (vno != 1) { ret = KRB5KDC_ERR_BAD_PVNO; numresult = KRB5_KPASSWD_MALFORMED; (void) snprintf(strresult, sizeof (strresult), "Request contained unknown protocol version number %d", vno); goto chpwfail; } /* * Read, check ap-req length */ ap_req.length = (*ptr++ & 0xff); ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff); if (ptr + ap_req.length >= req->data + req->length) { ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; (void) strlcpy(strresult, "Request was truncated in AP-REQ", sizeof (strresult)); goto chpwfail; } /* * Verify ap_req */ ap_req.data = ptr; ptr += ap_req.length; if (ret = krb5_auth_con_init(context, &auth_context)) { krb5_klog_syslog(LOG_ERR, gettext("Change password request failed. " "Failed initializing auth context: %s"), error_message(ret)); numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed initializing auth context", sizeof (strresult)); goto chpwfail; } if (ret = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE)) { krb5_klog_syslog(LOG_ERR, gettext("Change password request failed. " "Failed setting auth " "context flags: %s"), error_message(ret)); numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed initializing auth context", sizeof (strresult)); goto chpwfail; } if (ret = krb5_build_principal(context, &changepw, strlen(realm), realm, "kadmin", "changepw", NULL)) { krb5_klog_syslog(LOG_ERR, gettext("Change password request failed " "Failed to build kadmin/changepw " "principal: %s"), error_message(ret)); numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed building kadmin/changepw principal", sizeof (strresult)); goto chpwfail; } ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab, NULL, &ticket); if (ret) { char kt_name[MAX_KEYTAB_NAME_LEN]; if (krb5_kt_get_name(context, keytab, kt_name, sizeof (kt_name))) strncpy(kt_name, "default keytab", sizeof (kt_name)); switch (ret) { case KRB5_KT_NOTFOUND: krb5_klog_syslog(LOG_ERR, gettext("Change password request failed because " "keytab entry \"kadmin/changepw\" " "is missing from \"%s\""), kt_name); break; case ENOENT: krb5_klog_syslog(LOG_ERR, gettext("Change password request failed because " "keytab file \"%s\" does not exist"), kt_name); break; default: krb5_klog_syslog(LOG_ERR, gettext("Change password request failed. " "Failed to parse Kerberos AP_REQ message: %s"), error_message(ret)); } numresult = KRB5_KPASSWD_AUTHERROR; (void) strlcpy(strresult, "Failed reading application request", sizeof (strresult)); goto chpwfail; } /* * Set up address info */ addrlen = sizeof (local_addr); if (getsockname(s, &local_addr, &addrlen) < 0) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed getting server internet address", sizeof (strresult)); goto chpwfail; } /* * Some brain-dead OS's don't return useful information from * the getsockname call. Namely, windows and solaris. */ if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) { local_kaddr.addrtype = ADDRTYPE_INET; local_kaddr.length = sizeof (((struct sockaddr_in *) &local_addr)->sin_addr); /* CSTYLED */ local_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&local_addr)->sin_addr); } else { krb5_address **addrs; krb5_os_localaddr(context, &addrs); local_kaddr.magic = addrs[0]->magic; local_kaddr.addrtype = addrs[0]->addrtype; local_kaddr.length = addrs[0]->length; if ((local_kaddr.contents = malloc(addrs[0]->length)) == 0) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Malloc failed for local_kaddr", sizeof (strresult)); goto chpwfail; } (void) memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length); allocated_mem++; krb5_free_addresses(context, addrs); } addrlen = sizeof (remote_addr); if (getpeername(s, &remote_addr, &addrlen) < 0) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed getting client internet address", sizeof (strresult)); goto chpwfail; } remote_kaddr.addrtype = ADDRTYPE_INET; remote_kaddr.length = sizeof (((struct sockaddr_in *) &remote_addr)->sin_addr); /* CSTYLED */ remote_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&remote_addr)->sin_addr); remote_kaddr.addrtype = ADDRTYPE_INET; remote_kaddr.length = sizeof (sin->sin_addr); remote_kaddr.contents = (krb5_octet *) &sin->sin_addr; /* * mk_priv requires that the local address be set. * getsockname is used for this. rd_priv requires that the * remote address be set. recvfrom is used for this. If * rd_priv is given a local address, and the message has the * recipient addr in it, this will be checked. However, there * is simply no way to know ahead of time what address the * message will be delivered *to*. Therefore, it is important * that either no recipient address is in the messages when * mk_priv is called, or that no local address is passed to * rd_priv. Both is a better idea, and I have done that. In * summary, when mk_priv is called, *only* a local address is * specified. when rd_priv is called, *only* a remote address * is specified. Are we having fun yet? */ if (ret = krb5_auth_con_setaddrs(context, auth_context, NULL, &remote_kaddr)) { numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed storing client internet address", sizeof (strresult)); goto chpwfail; } /* * Verify that this is an AS_REQ ticket */ if (!(ticket->enc_part2->flags & TKT_FLG_INITIAL)) { numresult = KRB5_KPASSWD_AUTHERROR; (void) strlcpy(strresult, "Ticket must be derived from a password", sizeof (strresult)); goto chpwfail; } /* * Construct the ap-rep */ if (ret = krb5_mk_rep(context, auth_context, &ap_rep)) { numresult = KRB5_KPASSWD_AUTHERROR; (void) strlcpy(strresult, "Failed replying to application request", sizeof (strresult)); goto chpwfail; } /* * Decrypt the new password */ cipher.length = (req->data + req->length) - ptr; cipher.data = ptr; if (ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay)) { numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed decrypting request", sizeof (strresult)); goto chpwfail; } ret = krb5_unparse_name(context, ticket->enc_part2->client, &clientstr); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; (void) strcpy(strresult, "Failed decrypting request"); goto chpwfail; } /* * Change the password */ if ((ptr = (char *)malloc(clear.length + 1)) == NULL) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Malloc failed for ptr", sizeof (strresult)); krb5_free_unparsed_name(context, clientstr); goto chpwfail; } (void) memcpy(ptr, clear.data, clear.length); ptr[clear.length] = '\0'; ret = (kadm5_ret_t)kadm5_chpass_principal_util(server_handle, ticket->enc_part2->client, ptr, NULL, strresult, sizeof (strresult)); /* * Zap the password */ (void) memset(clear.data, 0, clear.length); (void) memset(ptr, 0, clear.length); if (clear.data != NULL) { krb5_xfree(clear.data); clear.data = NULL; } free(ptr); clear.length = 0; clen = strlen(clientstr); trunc_name(&clen, &cdots); krb5_klog_syslog(LOG_NOTICE, "chpw request from %s for %.*s%s: %s", inet_ntoa(((struct sockaddr_in *)&remote_addr)->sin_addr), clen, clientstr, cdots, ret ? error_message(ret) : "success"); krb5_free_unparsed_name(context, clientstr); if (ret) { if ((ret != KADM5_PASS_Q_TOOSHORT) && (ret != KADM5_PASS_REUSE) && (ret != KADM5_PASS_Q_CLASS) && (ret != KADM5_PASS_Q_DICT) && (ret != KADM5_PASS_TOOSOON)) numresult = KRB5_KPASSWD_HARDERROR; else numresult = KRB5_KPASSWD_SOFTERROR; /* * strresult set by kadb5_chpass_principal_util() */ goto chpwfail; } /* * Success! */ numresult = KRB5_KPASSWD_SUCCESS; (void) strlcpy(strresult, "", sizeof (strresult)); chpwfail: clear.length = 2 + strlen(strresult); if (clear.data != NULL) { krb5_xfree(clear.data); clear.data = NULL; } if ((clear.data = (char *)malloc(clear.length)) == NULL) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Malloc failed for clear.data", sizeof (strresult)); } ptr = clear.data; *ptr++ = (numresult>>8) & 0xff; *ptr++ = numresult & 0xff; (void) memcpy(ptr, strresult, strlen(strresult)); cipher.length = 0; if (ap_rep.length) { if (ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL)) { numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed storing client and server internet addresses", sizeof (strresult)); } else { if (ret = krb5_mk_priv(context, auth_context, &clear, &cipher, &replay)) { numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed encrypting reply", sizeof (strresult)); } } } /* * If no KRB-PRIV was constructed, then we need a KRB-ERROR. * If this fails, just bail. There's nothing else we can do. */ if (cipher.length == 0) { /* * Clear out ap_rep now, so that it won't be inserted * in the reply */ if (ap_rep.length) { if (ap_rep.data != NULL) krb5_xfree(ap_rep.data); ap_rep.data = NULL; ap_rep.length = 0; } krberror.ctime = 0; krberror.cusec = 0; krberror.susec = 0; if (ret = krb5_timeofday(context, &krberror.stime)) goto bailout; /* * This is really icky. but it's what all the other callers * to mk_error do. */ krberror.error = ret; krberror.error -= ERROR_TABLE_BASE_krb5; if (krberror.error < 0 || krberror.error > 128) krberror.error = KRB_ERR_GENERIC; krberror.client = NULL; if (ret = krb5_build_principal(context, &krberror.server, strlen(realm), realm, "kadmin", "changepw", NULL)) { goto bailout; } krberror.text.length = 0; krberror.e_data = clear; ret = krb5_mk_error(context, &krberror, &cipher); krb5_free_principal(context, krberror.server); if (ret) goto bailout; } /* * Construct the reply */ rep->length = 6 + ap_rep.length + cipher.length; if ((rep->data = (char *)malloc(rep->length)) == NULL) { ret = errno; goto bailout; } ptr = rep->data; /* * Length */ *ptr++ = (rep->length>>8) & 0xff; *ptr++ = rep->length & 0xff; /* * Version == 0x0001 big-endian */ *ptr++ = 0; *ptr++ = 1; /* * ap_rep length, big-endian */ *ptr++ = (ap_rep.length>>8) & 0xff; *ptr++ = ap_rep.length & 0xff; /* * ap-rep data */ if (ap_rep.length) { (void) memcpy(ptr, ap_rep.data, ap_rep.length); ptr += ap_rep.length; } /* * krb-priv or krb-error */ (void) memcpy(ptr, cipher.data, cipher.length); bailout: if (auth_context) krb5_auth_con_free(context, auth_context); if (changepw) krb5_free_principal(context, changepw); if (ap_rep.data != NULL) krb5_xfree(ap_rep.data); if (ticket) krb5_free_ticket(context, ticket); if (clear.data != NULL) krb5_xfree(clear.data); if (cipher.data != NULL) krb5_xfree(cipher.data); if (allocated_mem) krb5_xfree(local_kaddr.contents); return (ret); } /* * This routine is used to handle password-change requests received * on kpasswd-port 464 from MIT/M$ clients. */ void handle_chpw(krb5_context context, int s1, void *serverhandle, kadm5_config_params *params) { krb5_error_code ret; char req[MAXAPREQ]; int len; struct sockaddr_in from; int fromlen; krb5_keytab kt; krb5_data reqdata, repdata; int s2 = -1; reqdata.length = 0; reqdata.data = NULL; repdata.length = 0; repdata.data = NULL; fromlen = sizeof (from); if ((len = recvfrom(s1, req, sizeof (req), 0, (struct sockaddr *)&from, &fromlen)) < 0) { krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't receive " "request: %s"), error_message(errno)); return; } /* * Solaris Kerberos: * The only caller is kadmind, which is the master and therefore has the * correct keys in the KDB, rather than obtaining them via the * kadm5.keytab, by default. */ if ((ret = krb5_kt_resolve(context, "KDB:", &kt))) { krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't open " "admin keytab %s"), error_message(ret)); return; } reqdata.length = len; reqdata.data = req; /* * This is really obscure. s1 is used for all communications. it * is left unconnected in case the server is multihomed and routes * are asymmetric. s2 is connected to resolve routes and get * addresses. this is the *only* way to get proper addresses for * multihomed hosts if routing is asymmetric. * * A related problem in the server, but not the client, is that * many os's have no way to disconnect a connected udp socket, so * the s2 socket needs to be closed and recreated for each * request. The s1 socket must not be closed, or else queued * requests will be lost. * * A "naive" client implementation (one socket, no connect, * hostname resolution to get the local ip addr) will work and * interoperate if the client is single-homed. */ if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { krb5_klog_syslog(LOG_ERR, gettext("chpw: Cannot create " "connecting socket: %s"), error_message(errno)); goto cleanup; } if (connect(s2, (struct sockaddr *)&from, sizeof (from)) < 0) { krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't connect " "to client: %s"), error_message(errno)); if (s2 > 0) (void) close(s2); goto cleanup; } if ((ret = process_chpw_request(context, serverhandle, params->realm, s2, kt, &from, &reqdata, &repdata))) { krb5_klog_syslog(LOG_ERR, gettext("chpw: Error processing " "request: %s"), error_message(ret)); } if (s2 > 0) (void) close(s2); if (repdata.length == 0 || repdata.data == NULL) { /* * Just return. This means something really bad happened */ goto cleanup; } len = sendto(s1, repdata.data, repdata.length, 0, (struct sockaddr *)&from, sizeof (from)); if (len < repdata.length) { krb5_xfree(repdata.data); krb5_klog_syslog(LOG_ERR, gettext("chpw: Error sending reply:" " %s"), error_message(errno)); goto cleanup; } if (repdata.data != NULL) krb5_xfree(repdata.data); cleanup: krb5_kt_close(context, kt); } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * lib/kadm/logger.c * * Copyright 1995 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ /* KADM5 wants non-syslog log files to contain syslog-like entries */ #define VERBOSE_LOGS /* * logger.c - Handle logging functions for those who want it. */ #include "k5-int.h" #include "adm_proto.h" #include "com_err.h" #include #include #include #ifdef HAVE_SYSLOG_H #include #endif /* HAVE_SYSLOG_H */ #ifdef HAVE_STDARG_H #include #else /* HAVE_STDARG_H */ #include #endif /* HAVE_STDARG_H */ #include #include #include #define KRB5_KLOG_MAX_ERRMSG_SIZE 2048 #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 256 #endif /* MAXHOSTNAMELEN */ #define LSPEC_PARSE_ERR_1 1 #define LSPEC_PARSE_ERR_2 2 #define LOG_FILE_ERR 3 #define LOG_DEVICE_ERR 4 #define LOG_UFO_STRING 5 #define LOG_EMERG_STRING 6 #define LOG_ALERT_STRING 7 #define LOG_CRIT_STRING 8 #define LOG_ERR_STRING 9 #define LOG_WARNING_STRING 10 #define LOG_NOTICE_STRING 11 #define LOG_INFO_STRING 12 #define LOG_DEBUG_STRING 13 /* This is to assure that we have at least one match in the syslog stuff */ /* static const char LSPEC_PARSE_ERR_1[] = "%s: cannot parse <%s>\n"; static const char LSPEC_PARSE_ERR_2[] = "%s: warning - logging entry syntax error\n"; static const char LOG_FILE_ERR[] = "%s: error writing to %s\n"; static const char LOG_DEVICE_ERR[] = "%s: error writing to %s device\n"; static const char LOG_UFO_STRING[] = "???"; static const char LOG_EMERG_STRING[] = "EMERGENCY"; static const char LOG_ALERT_STRING[] = "ALERT"; static const char LOG_CRIT_STRING[] = "CRITICAL"; static const char LOG_ERR_STRING[] = "Error"; static const char LOG_WARNING_STRING[] = "Warning"; static const char LOG_NOTICE_STRING[] = "Notice"; static const char LOG_INFO_STRING[] = "info"; static const char LOG_DEBUG_STRING[] = "debug"; */ const char * krb5_log_error_table(long errorno) { switch (errorno) { case LSPEC_PARSE_ERR_1: return(gettext("%s: cannot parse <%s>\n")); case LSPEC_PARSE_ERR_2: return(gettext("%s: warning - logging entry syntax error\n")); case LOG_FILE_ERR: return(gettext("%s: error writing to %s\n")); case LOG_DEVICE_ERR: return(gettext("%s: error writing to %s device\n")); case LOG_UFO_STRING: default: return(gettext("???")); case LOG_EMERG_STRING: return(gettext("EMERGENCY")); case LOG_ALERT_STRING: return(gettext("ALERT")); case LOG_CRIT_STRING: return(gettext("CRITICAL")); case LOG_ERR_STRING: return(gettext("Error")); case LOG_WARNING_STRING: return(gettext("Warning")); case LOG_NOTICE_STRING: return(gettext("Notice")); case LOG_INFO_STRING: return(gettext("info")); case LOG_DEBUG_STRING: return(gettext("info")); } } /* * Output logging. * * Output logging is now controlled by the configuration file. We can specify * the following syntaxes under the [logging]->entity specification. * FILE * SYSLOG[=[:]] * STDERR * CONSOLE * DEVICE= * * Where: * is ":" for open/append, "=" for open/create. * is a valid path name. * is one of: (default = ERR) * EMERG * ALERT * CRIT * ERR * WARNING * NOTICE * INFO * DEBUG * is one of: (default = AUTH) * KERN * USER * MAIL * DAEMON * AUTH * LPR * NEWS * UUCP * CRON * LOCAL0..LOCAL7 * is a valid device specification. */ struct log_entry { enum log_type { K_LOG_FILE, K_LOG_SYSLOG, K_LOG_STDERR, K_LOG_CONSOLE, K_LOG_DEVICE, K_LOG_NONE } log_type; krb5_pointer log_2free; union log_union { struct log_file { FILE *lf_filep; char *lf_fname; char *lf_fopen_mode; /* "a+" or "w" */ #define K_LOG_DEF_FILE_ROTATE_PERIOD -1 /* never */ #define K_LOG_DEF_FILE_ROTATE_VERSIONS 0 /* no versions */ time_t lf_rotate_period; time_t lf_last_rotated; int lf_rotate_versions; } log_file; struct log_syslog { int ls_facility; int ls_severity; } log_syslog; struct log_device { FILE *ld_filep; char *ld_devname; } log_device; } log_union; }; #define lfu_filep log_union.log_file.lf_filep #define lfu_fname log_union.log_file.lf_fname #define lfu_fopen_mode log_union.log_file.lf_fopen_mode #define lfu_rotate_period log_union.log_file.lf_rotate_period #define lfu_last_rotated log_union.log_file.lf_last_rotated #define lfu_rotate_versions log_union.log_file.lf_rotate_versions #define lsu_facility log_union.log_syslog.ls_facility #define lsu_severity log_union.log_syslog.ls_severity #define ldu_filep log_union.log_device.ld_filep #define ldu_devname log_union.log_device.ld_devname struct log_control { struct log_entry *log_entries; int log_nentries; char *log_whoami; char *log_hostname; krb5_boolean log_opened; }; static struct log_control log_control = { (struct log_entry *) NULL, 0, (char *) NULL, (char *) NULL, 0 }; static struct log_entry def_log_entry; /* * These macros define any special processing that needs to happen for * devices. For unix, of course, this is hardly anything. */ #define DEVICE_OPEN(d, m) fopen(d, m) #define CONSOLE_OPEN(m) fopen("/dev/console", m) #define DEVICE_PRINT(f, m) ((fprintf(f, "%s\r\n", m) >= 0) ? \ (fflush(f), 0) : \ -1) #define DEVICE_CLOSE(d) fclose(d) /* * klog_rotate() - roate a log file if we have specified rotation * parameters in krb5.conf. */ static void klog_rotate(struct log_entry *le) { time_t t; int i; char *name_buf1; char *name_buf2; char *old_name; char *new_name; char *tmp; FILE *fp; int num_vers; mode_t old_umask; /* * By default we don't rotate. */ if (le->lfu_rotate_period == K_LOG_DEF_FILE_ROTATE_PERIOD) return; t = time(0); if (t >= le->lfu_last_rotated + le->lfu_rotate_period) { /* * The N log file versions will be renamed X.N-1 X.N-2, ... X.0. * So the allocate file name buffers that can the version * number extensions. * 32 extra bytes is plenty. */ name_buf1 = malloc(strlen(le->lfu_fname) + 32); if (name_buf1 == NULL) return; name_buf2 = malloc(strlen(le->lfu_fname) + 32); if (name_buf2 == NULL) { free(name_buf1); return; } old_name = name_buf1; new_name = name_buf2; /* * If there N versions, then the first one has file extension * of N-1. */ (void) sprintf(new_name, "%s.%d", le->lfu_fname, le->lfu_rotate_versions - 1); /* * Rename file.N-2 to file.N-1, file.N-3 to file.N-2, ... * file.0 to file.1 */ for (i = le->lfu_rotate_versions - 1; i > 0; i--) { (void) sprintf(old_name, "%s.%d", le->lfu_fname, i - 1); (void) rename(old_name, new_name); /* * swap old name and new name. This way, * on the next iteration, new_name.X * becomes new_name.X-1. */ tmp = old_name; old_name = new_name; new_name = tmp; } old_name = le->lfu_fname; (void) rename(old_name, new_name); /* * Even though we don't know yet if the fopen() * of the log file will succeed, we mark the log * as rotated. This is so we don't repeatably * rotate file.N-2 to file.N-1 ... etc without * waiting for the rotate period to elapse. */ le->lfu_last_rotated = t; /* * Default log file creation mode should be read-only * by owner(root), but the admin can override with * chmod(1) if desired. */ old_umask = umask(077); fp = fopen(old_name, le->lfu_fopen_mode); umask(old_umask); if (fp != NULL) { (void) fclose(le->lfu_filep); le->lfu_filep = fp; /* * If the version parameter in krb5.conf was * 0, then we take this to mean that rotating the * log file will cause us to dispose of the * old one, and created a new one. We have just * renamed the old one to file.-1, so remove it. */ if (le->lfu_rotate_versions <= 0) (void) unlink(new_name); } else { fprintf(stderr, gettext("During rotate, couldn't open log file %s: %s\n"), old_name, error_message(errno)); /* * Put it back. */ (void) rename(new_name, old_name); } free(name_buf1); free(name_buf2); } } /* * klog_com_err_proc() - Handle com_err(3) messages as specified by the * profile. */ static krb5_context err_context; static void klog_com_err_proc(const char *whoami, long code, const char *format, va_list ap) { char outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE]; int lindex; const char *actual_format; #ifdef HAVE_SYSLOG int log_pri = -1; #endif /* HAVE_SYSLOG */ char *cp; char *syslogp; /* Make the header */ sprintf(outbuf, "%s: ", whoami); /* * Squirrel away address after header for syslog since syslog makes * a header */ syslogp = &outbuf[strlen(outbuf)]; /* If reporting an error message, separate it. */ if (code) { /* Solaris Kerberos */ const char *emsg; outbuf[sizeof(outbuf) - 1] = '\0'; emsg = krb5_get_error_message (err_context, code); strncat(outbuf, emsg, sizeof(outbuf) - 1 - strlen(outbuf)); strncat(outbuf, " - ", sizeof(outbuf) - 1 - strlen(outbuf)); krb5_free_error_message(err_context, emsg); } cp = &outbuf[strlen(outbuf)]; actual_format = format; #ifdef HAVE_SYSLOG /* * This is an unpleasant hack. If the first character is less than * 8, then we assume that it is a priority. * * Since it is not guaranteed that there is a direct mapping between * syslog priorities (e.g. Ultrix and old BSD), we resort to this * intermediate representation. */ if ((((unsigned char) *format) > 0) && (((unsigned char) *format) <= 8)) { actual_format = (format + 1); switch ((unsigned char) *format) { #ifdef LOG_EMERG case 1: log_pri = LOG_EMERG; break; #endif /* LOG_EMERG */ #ifdef LOG_ALERT case 2: log_pri = LOG_ALERT; break; #endif /* LOG_ALERT */ #ifdef LOG_CRIT case 3: log_pri = LOG_CRIT; break; #endif /* LOG_CRIT */ default: case 4: log_pri = LOG_ERR; break; #ifdef LOG_WARNING case 5: log_pri = LOG_WARNING; break; #endif /* LOG_WARNING */ #ifdef LOG_NOTICE case 6: log_pri = LOG_NOTICE; break; #endif /* LOG_NOTICE */ #ifdef LOG_INFO case 7: log_pri = LOG_INFO; break; #endif /* LOG_INFO */ #ifdef LOG_DEBUG case 8: log_pri = LOG_DEBUG; break; #endif /* LOG_DEBUG */ } } #endif /* HAVE_SYSLOG */ /* Now format the actual message */ #if HAVE_VSNPRINTF vsnprintf(cp, sizeof(outbuf) - (cp - outbuf), actual_format, ap); #elif HAVE_VSPRINTF vsprintf(cp, actual_format, ap); #else /* HAVE_VSPRINTF */ sprintf(cp, actual_format, ((int *) ap)[0], ((int *) ap)[1], ((int *) ap)[2], ((int *) ap)[3], ((int *) ap)[4], ((int *) ap)[5]); #endif /* HAVE_VSPRINTF */ /* * Now that we have the message formatted, perform the output to each * logging specification. */ for (lindex = 0; lindex < log_control.log_nentries; lindex++) { switch (log_control.log_entries[lindex].log_type) { case K_LOG_FILE: klog_rotate(&log_control.log_entries[lindex]); /*FALLTHRU*/ case K_LOG_STDERR: /* * Files/standard error. */ if (fprintf(log_control.log_entries[lindex].lfu_filep, "%s\n", outbuf) < 0) { /* Attempt to report error */ fprintf(stderr, krb5_log_error_table(LOG_FILE_ERR), whoami, log_control.log_entries[lindex].lfu_fname); } else { fflush(log_control.log_entries[lindex].lfu_filep); } break; case K_LOG_CONSOLE: case K_LOG_DEVICE: /* * Devices (may need special handling) */ if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep, outbuf) < 0) { /* Attempt to report error */ fprintf(stderr, krb5_log_error_table(LOG_DEVICE_ERR), whoami, log_control.log_entries[lindex].ldu_devname); } break; #ifdef HAVE_SYSLOG case K_LOG_SYSLOG: /* * System log. */ /* * If we have specified a priority through our hackery, then * use it, otherwise use the default. */ if (log_pri >= 0) log_pri |= log_control.log_entries[lindex].lsu_facility; else log_pri = log_control.log_entries[lindex].lsu_facility | log_control.log_entries[lindex].lsu_severity; /* Log the message with our header trimmed off */ syslog(log_pri, "%s", syslogp); break; #endif /* HAVE_SYSLOG */ default: break; } } } /* * krb5_klog_init() - Initialize logging. * * This routine parses the syntax described above to specify destinations for * com_err(3) or krb5_klog_syslog() messages generated by the caller. * * Parameters: * kcontext - Kerberos context. * ename - Entity name as it is to appear in the profile. * whoami - Entity name as it is to appear in error output. * do_com_err - Take over com_err(3) processing. * * Implicit inputs: * stderr - This is where STDERR output goes. * * Implicit outputs: * log_nentries - Number of log entries, both valid and invalid. * log_control - List of entries (log_nentries long) which contains * data for klog_com_err_proc() to use to determine * where/how to send output. */ krb5_error_code krb5_klog_init(krb5_context kcontext, char *ename, char *whoami, krb5_boolean do_com_err) { const char *logging_profent[3]; const char *logging_defent[3]; char **logging_specs; int i, ngood; char *cp, *cp2; char savec = '\0'; int error; int do_openlog, log_facility; FILE *f; mode_t old_umask; /* Initialize */ do_openlog = 0; log_facility = 0; err_context = kcontext; /* * Look up [logging]-> in the profile. If that doesn't * succeed, then look for [logging]->default. */ logging_profent[0] = "logging"; logging_profent[1] = ename; logging_profent[2] = (char *) NULL; logging_defent[0] = "logging"; logging_defent[1] = "default"; logging_defent[2] = (char *) NULL; logging_specs = (char **) NULL; ngood = 0; log_control.log_nentries = 0; if (!profile_get_values(kcontext->profile, logging_profent, &logging_specs) || !profile_get_values(kcontext->profile, logging_defent, &logging_specs)) { /* * We have a match, so we first count the number of elements */ for (log_control.log_nentries = 0; logging_specs[log_control.log_nentries]; log_control.log_nentries++); /* * Now allocate our structure. */ log_control.log_entries = (struct log_entry *) malloc(log_control.log_nentries * sizeof(struct log_entry)); if (log_control.log_entries) { /* * Scan through the list. */ for (i=0; i * so, trim off the leading and trailing whitespace here. */ for (cp = logging_specs[i]; isspace((int) *cp); cp++); for (cp2 = &logging_specs[i][strlen(logging_specs[i])-1]; isspace((int) *cp2); cp2--); cp2++; *cp2 = '\0'; /* * Is this a file? */ if (!strncasecmp(cp, "FILE", 4)) { /* * Check for append/overwrite, then open the file. */ if (cp[4] == ':' || cp[4] == '=') { log_control.log_entries[i].lfu_fopen_mode = (cp[4] == ':') ? "a+F" : "wF"; old_umask = umask(077); f = fopen(&cp[5], log_control.log_entries[i].lfu_fopen_mode); umask(old_umask); if (f) { char rotate_kw[128]; log_control.log_entries[i].lfu_filep = f; log_control.log_entries[i].log_type = K_LOG_FILE; log_control.log_entries[i].lfu_fname = &cp[5]; log_control.log_entries[i].lfu_rotate_period = K_LOG_DEF_FILE_ROTATE_PERIOD; log_control.log_entries[i].lfu_rotate_versions = K_LOG_DEF_FILE_ROTATE_VERSIONS; log_control.log_entries[i].lfu_last_rotated = time(0); /* * Now parse for ename_"rotate" = { * period = XXX * versions = 10 * } */ if (strlen(ename) + strlen("_rotate") < sizeof (rotate_kw)) { char *time; krb5_deltat dt; int vers; strcpy(rotate_kw, ename); strcat(rotate_kw, "_rotate"); if (!profile_get_string(kcontext->profile, "logging", rotate_kw, "period", NULL, &time)) { if (time != NULL) { if (!krb5_string_to_deltat(time, &dt)) { log_control.log_entries[i].lfu_rotate_period = (time_t) dt; } free(time); } } if (!profile_get_integer( kcontext->profile, "logging", rotate_kw, "versions", K_LOG_DEF_FILE_ROTATE_VERSIONS, &vers)) { log_control.log_entries[i].lfu_rotate_versions = vers; } } } else { fprintf(stderr, gettext("Couldn't open log file %s: %s\n"), &cp[5], error_message(errno)); continue; } } } #ifdef HAVE_SYSLOG /* * Is this a syslog? */ else if (!strncasecmp(cp, "SYSLOG", 6)) { error = 0; log_control.log_entries[i].lsu_facility = LOG_AUTH; log_control.log_entries[i].lsu_severity = LOG_ERR; /* * Is there a severify specified? */ if (cp[6] == ':') { /* * Find the end of the severity. */ cp2 = strchr(&cp[7], ':'); if (cp2) { savec = *cp2; *cp2 = '\0'; cp2++; } /* * Match a severity. */ if (!strcasecmp(&cp[7], "ERR")) { log_control.log_entries[i].lsu_severity = LOG_ERR; } #ifdef LOG_EMERG else if (!strcasecmp(&cp[7], "EMERG")) { log_control.log_entries[i].lsu_severity = LOG_EMERG; } #endif /* LOG_EMERG */ #ifdef LOG_ALERT else if (!strcasecmp(&cp[7], "ALERT")) { log_control.log_entries[i].lsu_severity = LOG_ALERT; } #endif /* LOG_ALERT */ #ifdef LOG_CRIT else if (!strcasecmp(&cp[7], "CRIT")) { log_control.log_entries[i].lsu_severity = LOG_CRIT; } #endif /* LOG_CRIT */ #ifdef LOG_WARNING else if (!strcasecmp(&cp[7], "WARNING")) { log_control.log_entries[i].lsu_severity = LOG_WARNING; } #endif /* LOG_WARNING */ #ifdef LOG_NOTICE else if (!strcasecmp(&cp[7], "NOTICE")) { log_control.log_entries[i].lsu_severity = LOG_NOTICE; } #endif /* LOG_NOTICE */ #ifdef LOG_INFO else if (!strcasecmp(&cp[7], "INFO")) { log_control.log_entries[i].lsu_severity = LOG_INFO; } #endif /* LOG_INFO */ #ifdef LOG_DEBUG else if (!strcasecmp(&cp[7], "DEBUG")) { log_control.log_entries[i].lsu_severity = LOG_DEBUG; } #endif /* LOG_DEBUG */ else error = 1; /* * If there is a facility present, then parse that. */ if (cp2) { if (!strcasecmp(cp2, "AUTH")) { log_control.log_entries[i].lsu_facility = LOG_AUTH; } else if (!strcasecmp(cp2, "KERN")) { log_control.log_entries[i].lsu_facility = LOG_KERN; } else if (!strcasecmp(cp2, "USER")) { log_control.log_entries[i].lsu_facility = LOG_USER; } else if (!strcasecmp(cp2, "MAIL")) { log_control.log_entries[i].lsu_facility = LOG_MAIL; } else if (!strcasecmp(cp2, "DAEMON")) { log_control.log_entries[i].lsu_facility = LOG_DAEMON; } else if (!strcasecmp(cp2, "LPR")) { log_control.log_entries[i].lsu_facility = LOG_LPR; } else if (!strcasecmp(cp2, "NEWS")) { log_control.log_entries[i].lsu_facility = LOG_NEWS; } else if (!strcasecmp(cp2, "UUCP")) { log_control.log_entries[i].lsu_facility = LOG_UUCP; } else if (!strcasecmp(cp2, "CRON")) { log_control.log_entries[i].lsu_facility = LOG_CRON; } else if (!strcasecmp(cp2, "LOCAL0")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL0; } else if (!strcasecmp(cp2, "LOCAL1")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL1; } else if (!strcasecmp(cp2, "LOCAL2")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL2; } else if (!strcasecmp(cp2, "LOCAL3")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL3; } else if (!strcasecmp(cp2, "LOCAL4")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL4; } else if (!strcasecmp(cp2, "LOCAL5")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL5; } else if (!strcasecmp(cp2, "LOCAL6")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL6; } else if (!strcasecmp(cp2, "LOCAL7")) { log_control.log_entries[i].lsu_facility = LOG_LOCAL7; } cp2--; *cp2 = savec; } } if (!error) { log_control.log_entries[i].log_type = K_LOG_SYSLOG; do_openlog = 1; log_facility = log_control.log_entries[i].lsu_facility; } } #endif /* HAVE_SYSLOG */ /* * Is this a standard error specification? */ else if (!strcasecmp(cp, "STDERR")) { log_control.log_entries[i].lfu_filep = fdopen(fileno(stderr), "a+F"); if (log_control.log_entries[i].lfu_filep) { log_control.log_entries[i].log_type = K_LOG_STDERR; log_control.log_entries[i].lfu_fname = "standard error"; } } /* * Is this a specification of the console? */ else if (!strcasecmp(cp, "CONSOLE")) { log_control.log_entries[i].ldu_filep = CONSOLE_OPEN("a+F"); if (log_control.log_entries[i].ldu_filep) { log_control.log_entries[i].log_type = K_LOG_CONSOLE; log_control.log_entries[i].ldu_devname = "console"; } } /* * Is this a specification of a device? */ else if (!strncasecmp(cp, "DEVICE", 6)) { /* * We handle devices very similarly to files. */ if (cp[6] == '=') { log_control.log_entries[i].ldu_filep = DEVICE_OPEN(&cp[7], "wF"); if (log_control.log_entries[i].ldu_filep) { log_control.log_entries[i].log_type = K_LOG_DEVICE; log_control.log_entries[i].ldu_devname = &cp[7]; } } } /* * See if we successfully parsed this specification. */ if (log_control.log_entries[i].log_type == K_LOG_NONE) { fprintf(stderr, krb5_log_error_table(LSPEC_PARSE_ERR_1), whoami, cp); fprintf(stderr, krb5_log_error_table(LSPEC_PARSE_ERR_2), whoami); } else ngood++; } } /* * If we didn't find anything, then free our lists. */ if (ngood == 0) { for (i=0; ilog_type = K_LOG_SYSLOG; log_control.log_entries->log_2free = (krb5_pointer) NULL; log_facility = log_control.log_entries->lsu_facility = LOG_AUTH; log_control.log_entries->lsu_severity = LOG_ERR; do_openlog = 1; log_control.log_nentries = 1; } if (log_control.log_nentries) { log_control.log_whoami = (char *) malloc(strlen(whoami)+1); if (log_control.log_whoami) strcpy(log_control.log_whoami, whoami); log_control.log_hostname = (char *) malloc(MAXHOSTNAMELEN + 1); if (log_control.log_hostname) { gethostname(log_control.log_hostname, MAXHOSTNAMELEN); log_control.log_hostname[MAXHOSTNAMELEN] = '\0'; } #ifdef HAVE_OPENLOG if (do_openlog) { openlog(whoami, LOG_NDELAY|LOG_PID, log_facility); log_control.log_opened = 1; } #endif /* HAVE_OPENLOG */ if (do_com_err) (void) set_com_err_hook(klog_com_err_proc); } return((log_control.log_nentries) ? 0 : ENOENT); } /* * krb5_klog_close() - Close the logging context and free all data. */ void krb5_klog_close(krb5_context kcontext) { int lindex; (void) reset_com_err_hook(); for (lindex = 0; lindex < log_control.log_nentries; lindex++) { switch (log_control.log_entries[lindex].log_type) { case K_LOG_FILE: case K_LOG_STDERR: /* * Files/standard error. */ fclose(log_control.log_entries[lindex].lfu_filep); break; case K_LOG_CONSOLE: case K_LOG_DEVICE: /* * Devices (may need special handling) */ DEVICE_CLOSE(log_control.log_entries[lindex].ldu_filep); break; #ifdef HAVE_SYSLOG case K_LOG_SYSLOG: /* * System log. */ break; #endif /* HAVE_SYSLOG */ default: break; } if (log_control.log_entries[lindex].log_2free) free(log_control.log_entries[lindex].log_2free); } if (log_control.log_entries != &def_log_entry) free(log_control.log_entries); log_control.log_entries = (struct log_entry *) NULL; log_control.log_nentries = 0; if (log_control.log_whoami) free(log_control.log_whoami); log_control.log_whoami = (char *) NULL; if (log_control.log_hostname) free(log_control.log_hostname); log_control.log_hostname = (char *) NULL; #ifdef HAVE_CLOSELOG if (log_control.log_opened) closelog(); #endif /* HAVE_CLOSELOG */ } /* * severity2string() - Convert a severity to a string. */ static const char * severity2string(int severity) { int s; const char *ss; s = severity & LOG_PRIMASK; ss = krb5_log_error_table(LOG_UFO_STRING); switch (s) { #ifdef LOG_EMERG case LOG_EMERG: ss = krb5_log_error_table(LOG_EMERG_STRING); break; #endif /* LOG_EMERG */ #ifdef LOG_ALERT case LOG_ALERT: ss = krb5_log_error_table(LOG_ALERT_STRING); break; #endif /* LOG_ALERT */ #ifdef LOG_CRIT case LOG_CRIT: ss = krb5_log_error_table(LOG_CRIT_STRING); break; #endif /* LOG_CRIT */ case LOG_ERR: ss = krb5_log_error_table(LOG_ERR_STRING); break; #ifdef LOG_WARNING case LOG_WARNING: ss = krb5_log_error_table(LOG_WARNING_STRING); break; #endif /* LOG_WARNING */ #ifdef LOG_NOTICE case LOG_NOTICE: ss = krb5_log_error_table(LOG_NOTICE_STRING); break; #endif /* LOG_NOTICE */ #ifdef LOG_INFO case LOG_INFO: ss = krb5_log_error_table(LOG_INFO_STRING); break; #endif /* LOG_INFO */ #ifdef LOG_DEBUG case LOG_DEBUG: ss = krb5_log_error_table(LOG_DEBUG_STRING); break; #endif /* LOG_DEBUG */ } return((char *) ss); } /* * krb5_klog_syslog() - Simulate the calling sequence of syslog(3), while * also performing the logging redirection as specified * by krb5_klog_init(). */ static int klog_vsyslog(int priority, const char *format, va_list arglist) { char outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE]; int lindex; char *syslogp; char *cp; time_t now; #ifdef HAVE_STRFTIME size_t soff; #endif /* HAVE_STRFTIME */ /* * Format a syslog-esque message of the format: * * (verbose form) * [](): * * (short form) * */ cp = outbuf; (void) time(&now); #ifdef HAVE_STRFTIME /* * Format the date: mon dd hh:mm:ss */ soff = strftime(outbuf, sizeof(outbuf), "%b %d %H:%M:%S", localtime(&now)); if (soff > 0) cp += soff; else return(-1); #else /* HAVE_STRFTIME */ /* * Format the date: * We ASSUME here that the output of ctime is of the format: * dow mon dd hh:mm:ss tzs yyyy\n * 012345678901234567890123456789 */ strncpy(outbuf, ctime(&now) + 4, 15); cp += 15; #endif /* HAVE_STRFTIME */ #ifdef VERBOSE_LOGS sprintf(cp, " %s %s[%ld](%s): ", log_control.log_hostname, log_control.log_whoami, (long) getpid(), severity2string(priority)); #else sprintf(cp, " "); #endif syslogp = &outbuf[strlen(outbuf)]; /* Now format the actual message */ #ifdef HAVE_VSNPRINTF vsnprintf(syslogp, sizeof(outbuf) - (syslogp - outbuf), format, arglist); #elif HAVE_VSPRINTF vsprintf(syslogp, format, arglist); #else /* HAVE_VSPRINTF */ sprintf(syslogp, format, ((int *) arglist)[0], ((int *) arglist)[1], ((int *) arglist)[2], ((int *) arglist)[3], ((int *) arglist)[4], ((int *) arglist)[5]); #endif /* HAVE_VSPRINTF */ /* * If the user did not use krb5_klog_init() instead of dropping * the request on the floor, syslog it - if it exists */ #ifdef HAVE_SYSLOG if (log_control.log_nentries == 0) { /* Log the message with our header trimmed off */ syslog(priority, "%s", syslogp); } #endif /* * Now that we have the message formatted, perform the output to each * logging specification. */ for (lindex = 0; lindex < log_control.log_nentries; lindex++) { switch (log_control.log_entries[lindex].log_type) { case K_LOG_FILE: klog_rotate(&log_control.log_entries[lindex]); /*FALLTHRU*/ case K_LOG_STDERR: /* * Files/standard error. */ if (fprintf(log_control.log_entries[lindex].lfu_filep, "%s\n", outbuf) < 0) { /* Attempt to report error */ fprintf(stderr, krb5_log_error_table(LOG_FILE_ERR), log_control.log_whoami, log_control.log_entries[lindex].lfu_fname); } else { fflush(log_control.log_entries[lindex].lfu_filep); } break; case K_LOG_CONSOLE: case K_LOG_DEVICE: /* * Devices (may need special handling) */ if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep, outbuf) < 0) { /* Attempt to report error */ fprintf(stderr, krb5_log_error_table(LOG_DEVICE_ERR), log_control.log_whoami, log_control.log_entries[lindex].ldu_devname); } break; #ifdef HAVE_SYSLOG case K_LOG_SYSLOG: /* * System log. */ /* Log the message with our header trimmed off */ syslog(priority, "%s", syslogp); break; #endif /* HAVE_SYSLOG */ default: break; } } return(0); } int krb5_klog_syslog(int priority, const char *format, ...) { int retval; va_list pvar; va_start(pvar, format); retval = klog_vsyslog(priority, format, pvar); va_end(pvar); return(retval); } /* * krb5_klog_reopen() - Close and reopen any open (non-syslog) log files. * This function is called when a SIGHUP is received * so that external log-archival utilities may * alert the Kerberos daemons that they should get * a new file descriptor for the give filename. */ void krb5_klog_reopen(krb5_context kcontext) { int lindex; FILE *f; /* * Only logs which are actually files need to be closed * and reopened in response to a SIGHUP */ for (lindex = 0; lindex < log_control.log_nentries; lindex++) { if (log_control.log_entries[lindex].log_type == K_LOG_FILE) { fclose(log_control.log_entries[lindex].lfu_filep); /* * In case the old logfile did not get moved out of the * way, open for append to prevent squashing the old logs. */ f = fopen(log_control.log_entries[lindex].lfu_fname, "a+F"); if (f) { log_control.log_entries[lindex].lfu_filep = f; } else { fprintf(stderr, "Couldn't open log file %s: %s\n", log_control.log_entries[lindex].lfu_fname, error_message(errno)); } } } } /* * Solaris Kerberos: * Switch the current context to the one supplied */ void krb5_klog_set_context(krb5_context context) { err_context = context; } /* * Solaris Kerberos: * Return a string representation of "facility" */ static const char * facility2string(int facility) { switch (facility) { case (LOG_AUTH): return ("AUTH"); case (LOG_KERN): return ("KERN"); case (LOG_USER): return ("USER"); case (LOG_MAIL): return ("MAIL"); case (LOG_DAEMON): return ("DAEMON"); case (LOG_LPR): return ("LPR"); case (LOG_NEWS): return ("NEWS"); case (LOG_UUCP): return ("UUCP"); case (LOG_CRON): return ("CRON"); case (LOG_LOCAL0): return ("LOCAL0"); case (LOG_LOCAL1): return ("LOCAL1"); case (LOG_LOCAL2): return ("LOCAL2"); case (LOG_LOCAL3): return ("LOCAL3"); case (LOG_LOCAL4): return ("LOCAL4"); case (LOG_LOCAL5): return ("LOCAL6"); case (LOG_LOCAL7): return ("LOCAL7"); } return ("UNKNOWN"); } /* * Solaris Kerberos: * Print to stderr where logging is being done */ krb5_error_code krb5_klog_list_logs(const char *whoami) { int lindex; fprintf(stderr, gettext("%s: logging to "), whoami); for (lindex = 0; lindex < log_control.log_nentries; lindex++) { if (lindex != 0 && log_control.log_entries[lindex].log_type != K_LOG_NONE) fprintf(stderr, ", "); switch (log_control.log_entries[lindex].log_type) { case K_LOG_FILE: fprintf(stderr, "FILE=%s", log_control.log_entries[lindex].lfu_fname); break; case K_LOG_STDERR: fprintf(stderr, "STDERR"); break; case K_LOG_CONSOLE: fprintf(stderr, "CONSOLE"); break; case K_LOG_DEVICE: fprintf(stderr, "DEVICE=%s", log_control.log_entries[lindex].ldu_devname); break; case K_LOG_SYSLOG: fprintf(stderr, "SYSLOG=%s:%s", severity2string(log_control.log_entries[lindex].lsu_severity), facility2string(log_control.log_entries[lindex].lsu_facility)); break; case K_LOG_NONE: break; default: /* Should never get here */ return (-1); } } fprintf(stderr, "\n"); return (0); } /* * Solaris Kerberos: * Add logging to stderr. */ krb5_error_code krb5_klog_add_stderr() { struct log_entry *tmp_log_entries = log_control.log_entries; int i; if (log_control.log_entries != &def_log_entry) { log_control.log_entries = realloc(log_control.log_entries, (log_control.log_nentries + 1) * sizeof(struct log_entry)); if (log_control.log_entries == NULL) { log_control.log_entries = tmp_log_entries; return (ENOMEM); } } else { log_control.log_entries = malloc(2 * sizeof(struct log_entry)); if (log_control.log_entries == NULL) { log_control.log_entries = &def_log_entry; return (ENOMEM); } (void) memcpy(&log_control.log_entries[0], &def_log_entry, sizeof(struct log_entry)); } i = log_control.log_nentries; if (log_control.log_entries[i].lfu_filep = fdopen(fileno(stderr), "a+F")) { log_control.log_entries[i].log_type = K_LOG_STDERR; log_control.log_entries[i].log_2free = NULL; log_control.log_entries[i].lfu_fname = "standard error"; log_control.log_nentries++; } else { /* Free the alloc'ed extra entry */ int err = errno; tmp_log_entries = log_control.log_entries; log_control.log_entries = realloc(log_control.log_entries, (log_control.log_nentries) * sizeof(struct log_entry)); if (log_control.log_entries == NULL) log_control.log_entries = tmp_log_entries; return (err); } return (0); } /* * Solaris Kerberos * Remove logging to stderr. */ void krb5_klog_remove_stderr() { struct log_entry *tmp_log_entries = log_control.log_entries; int i; /* Find the entry (if it exists) */ for (i = 0; i < log_control.log_nentries; i++) { if (log_control.log_entries[i].log_type == K_LOG_STDERR) { break; } } if ( i < log_control.log_nentries) { for (; i < log_control.log_nentries - 1; i++) log_control.log_entries[i] = log_control.log_entries[i + 1]; if (log_control.log_nentries > 1) { log_control.log_entries = realloc(log_control.log_entries, (log_control.log_nentries + 1) * sizeof(struct log_entry)); if (log_control.log_entries != NULL) log_control.log_nentries--; else log_control.log_entries = tmp_log_entries; } else { if (log_control.log_entries != NULL) free(log_control.log_entries); } } } /* Solaris Kerberos: Indicate if currently logging to stderr */ krb5_boolean krb5_klog_logging_to_stderr() { int i; /* Find the entry (if it exists) */ for (i = 0; i < log_control.log_nentries; i++) { if (log_control.log_entries[i].log_type == K_LOG_STDERR) { return (TRUE); } } return (FALSE); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # # Due to mistakes made early in the history of this library, there are # no SUNW_1.1 symbols, but the version is now kept as a placeholder. # Don't add any symbols to this version. $mapfile_version 2 SYMBOL_VERSION SUNW_1.1 { global: SUNW_1.1; }; SYMBOL_VERSION SUNWprivate_1.1 { global: __kadm5_get_priv; _kadm5_get_kpasswd_protocol; adb_policy_close; adb_policy_init; destroy_dict; find_word; handle_chpw; hist_db { ASSERT = { TYPE = OBJECT; SIZE = 68; }; }; hist_key { ASSERT = { TYPE = OBJECT; SIZE = 28; }; }; hist_kvno { ASSERT = { TYPE = OBJECT; SIZE = 4; }; }; hist_princ { ASSERT = { TYPE = OBJECT; SIZE = 4; }; }; init_dict; kadm5_check_min_life; kadm5_chpass_principal; kadm5_chpass_principal_3; kadm5_chpass_principal_util; kadm5_create_policy; kadm5_create_policy_internal; kadm5_create_principal; kadm5_create_principal_3; kadm5_decrypt_key; kadm5_delete_policy; kadm5_delete_principal; kadm5_destroy; kadm5_flush; kadm5_free_config_params; kadm5_free_name_list; kadm5_free_policy_ent; kadm5_free_principal_ent; kadm5_get_adm_host_srv_name; kadm5_get_config_params; kadm5_get_cpw_host_srv_name; kadm5_get_kiprop_host_srv_name; kadm5_get_master; kadm5_get_policies; kadm5_get_policy; kadm5_get_principal; kadm5_get_principals; kadm5_get_privs; kadm5_init2; kadm5_init; kadm5_init_iprop; kadm5_init_krb5_context; kadm5_init_with_creds; kadm5_init_with_password; kadm5_is_master; kadm5_lock; kadm5_modify_policy; kadm5_modify_policy_internal; kadm5_modify_principal; kadm5_randkey_principal; kadm5_randkey_principal_3; kadm5_rename_principal; kadm5_setkey_principal; kadm5_setkey_principal_3; kadm5_unlock; kadm5int_acl_check; kadm5int_acl_finish; kadm5int_acl_impose_restrictions; kadm5int_acl_init; kadm5_init_with_skey; kdb_delete_entry; kdb_free_entry; kdb_get_entry; kdb_init_hist; kdb_init_master; kdb_iter_entry; kdb_put_entry; krb5_aprof_finish; krb5_aprof_get_boolean; krb5_aprof_get_deltat; krb5_aprof_get_int32; krb5_aprof_get_string; krb5_aprof_getvals; krb5_aprof_init; krb5_copy_key_data_contents; krb5_flags_to_string; krb5_free_key_data_contents; krb5_free_realm_params; krb5_input_flag_to_string; krb5_keysalt_is_present; krb5_keysalt_iterate; krb5_klog_add_stderr; krb5_klog_close; krb5_klog_init; krb5_klog_list_logs; krb5_klog_logging_to_stderr; krb5_klog_remove_stderr; krb5_klog_reopen; krb5_klog_set_context; krb5_klog_syslog; krb5_read_realm_params; krb5_string_to_flags; krb5_string_to_keysalts; master_db { ASSERT = { TYPE = OBJECT; SIZE = 68; }; }; master_princ { ASSERT = { TYPE = OBJECT; SIZE = 4; }; }; osa_free_princ_ent; passwd_check; trunc_name; xdr_chpass3_arg; xdr_chpass_arg; xdr_chrand3_arg; xdr_chrand_arg; xdr_chrand_ret; xdr_cpol_arg; xdr_cprinc3_arg; xdr_cprinc_arg; xdr_dpol_arg; xdr_dprinc_arg; xdr_generic_ret; xdr_getprivs_ret; xdr_gpol_arg; xdr_gpol_ret; xdr_gpols_arg; xdr_gpols_ret; xdr_gprinc_arg; xdr_gprinc_ret; xdr_gprincs_arg; xdr_gprincs_ret; xdr_kadm5_policy_ent_rec; xdr_kadm5_principal_ent_rec; xdr_kadm5_principal_ent_rec_v1; xdr_kadm5_ret_t; xdr_krb5_deltat; xdr_krb5_enctype; xdr_krb5_flags; xdr_krb5_int16; xdr_krb5_key_data; xdr_krb5_key_data_nocontents; xdr_krb5_key_salt_tuple; xdr_krb5_keyblock; xdr_krb5_kvno; xdr_krb5_octet; xdr_krb5_principal; xdr_krb5_salttype; xdr_krb5_timestamp; xdr_krb5_tl_data; xdr_krb5_ui_2; xdr_krb5_ui_4; xdr_mpol_arg; xdr_mprinc_arg; xdr_nullstring; xdr_nulltype; xdr_osa_princ_ent_rec; xdr_osa_pw_hist_ent; xdr_rprinc_arg; xdr_setkey3_arg; xdr_setkey_arg; xdr_ui_4; xdralloc_create; xdralloc_getdata; xdralloc_release; local: *; }; /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * kadmin/v5server/srv_acl.c * * Copyright 1995 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ /* * srv_acl.c - Handle Kerberos ACL related functions. */ #include #include #include #include "k5-int.h" #include #include #include #include /* SUNWresync121 XXX */ #include "server_acl.h" #include #include /* SUNWresync121 XXX */ typedef struct _acl_op_table { char ao_op; krb5_int32 ao_mask; } aop_t; typedef struct _acl_entry { struct _acl_entry *ae_next; char *ae_name; krb5_boolean ae_name_bad; krb5_principal ae_principal; krb5_int32 ae_op_allowed; char *ae_target; krb5_boolean ae_target_bad; krb5_principal ae_target_princ; char *ae_restriction_string; /* eg: "-maxlife 3h -service +proxiable" */ krb5_boolean ae_restriction_bad; restriction_t *ae_restrictions; } aent_t; static const aop_t acl_op_table[] = { { 'a', ACL_ADD }, { 'd', ACL_DELETE }, { 'm', ACL_MODIFY }, { 'c', ACL_CHANGEPW }, { 'i', ACL_INQUIRE }, { 'l', ACL_LIST }, { 'p', ACL_IPROP }, /* SUNW IProp */ { 's', ACL_SETKEY }, { 'u', ACL_MIGRATE }, /* pam_krb5_migrate */ { 'x', ACL_ALL_MASK }, { '*', ACL_ALL_MASK }, { '\0', 0 } }; typedef struct _wildstate { int nwild; krb5_data *backref[9]; } wildstate_t; static aent_t *acl_list_head = (aent_t *) NULL; static aent_t *acl_list_tail = (aent_t *) NULL; static const char *acl_acl_file = (char *) NULL; static int acl_inited = 0; static int acl_debug_level = 0; /* * This is the catchall entry. If nothing else appropriate is found, or in * the case where the ACL file is not present, this entry controls what can * be done. */ static const char *acl_catchall_entry = NULL; /* Solaris Kerberos */ #define ACL_LINE2LONG_MSG dgettext(TEXT_DOMAIN, \ "%s: line %d too long, truncated\n") #define ACL_OP_BAD_MSG dgettext(TEXT_DOMAIN, \ "Unrecognized ACL operation '%c' in %s\n") #define ACL_SYN_ERR_MSG dgettext(TEXT_DOMAIN, \ "%s: syntax error at line %d <%10s...>\n") #define ACL_CANTOPEN_MSG dgettext(TEXT_DOMAIN, \ "\007cannot open ACL file") /* * kadm5int_acl_get_line() - Get a line from the ACL file. * Lines ending with \ are continued on the next line */ static char * kadm5int_acl_get_line(fp, lnp) FILE *fp; int *lnp; /* caller should set to 1 before first call */ { int i, domore; static int line_incr = 0; static char acl_buf[BUFSIZ]; *lnp += line_incr; line_incr = 0; for (domore = 1; domore && !feof(fp); ) { /* Copy in the line, with continuations */ for (i=0; ((i < sizeof acl_buf) && !feof(fp)); i++ ) { acl_buf[i] = fgetc(fp); if (acl_buf[i] == (char)EOF) { if (i > 0 && acl_buf[i-1] == '\\') i--; break; /* it gets nulled-out below */ } else if (acl_buf[i] == '\n') { if (i == 0 || acl_buf[i-1] != '\\') break; /* empty line or normal end of line */ else { i -= 2; /* back up over "\\\n" and continue */ line_incr++; } } } /* Check if we exceeded our buffer size */ if (i == sizeof acl_buf && (i--, !feof(fp))) { int c1 = acl_buf[i], c2; krb5_klog_syslog(LOG_ERR, ACL_LINE2LONG_MSG, acl_acl_file, *lnp); while ((c2 = fgetc(fp)) != EOF) { if (c2 == '\n') { if (c1 != '\\') break; line_incr++; } c1 = c2; } } acl_buf[i] = '\0'; if (acl_buf[0] == (char) EOF) /* ptooey */ acl_buf[0] = '\0'; else line_incr++; if ((acl_buf[0] != '#') && (acl_buf[0] != '\0')) domore = 0; } if (domore || (strlen(acl_buf) == 0)) return((char *) NULL); else return(acl_buf); } /* * kadm5int_acl_parse_line() - Parse the contents of an ACL line. */ static aent_t * kadm5int_acl_parse_line(lp) const char *lp; { static char acle_principal[BUFSIZ]; static char acle_ops[BUFSIZ]; static char acle_object[BUFSIZ]; static char acle_restrictions[BUFSIZ]; aent_t *acle; char *op; int t, found, opok, nmatch; DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_parse_line(line=%20s)\n", lp)); /* * Format is still simple: * entry ::= [] * [ [ * []]] */ acle = (aent_t *) NULL; acle_object[0] = '\0'; nmatch = sscanf(lp, "%s %s %s %[^\n]", acle_principal, acle_ops, acle_object, acle_restrictions); if (nmatch >= 2) { acle = (aent_t *) malloc(sizeof(aent_t)); if (acle) { acle->ae_next = (aent_t *) NULL; acle->ae_op_allowed = (krb5_int32) 0; acle->ae_target = (nmatch >= 3) ? strdup(acle_object) : (char *) NULL; acle->ae_target_bad = 0; acle->ae_target_princ = (krb5_principal) NULL; opok = 1; for (op=acle_ops; *op; op++) { char rop; rop = (isupper((unsigned char) *op)) ? tolower((unsigned char) *op) : *op; found = 0; for (t=0; acl_op_table[t].ao_op; t++) { if (rop == acl_op_table[t].ao_op) { found = 1; if (rop == *op) acle->ae_op_allowed |= acl_op_table[t].ao_mask; else acle->ae_op_allowed &= ~acl_op_table[t].ao_mask; } } if (!found) { krb5_klog_syslog(LOG_ERR, ACL_OP_BAD_MSG, *op, lp); opok = 0; } } if (opok) { acle->ae_name = (char *) malloc(strlen(acle_principal)+1); if (acle->ae_name) { strcpy(acle->ae_name, acle_principal); acle->ae_principal = (krb5_principal) NULL; acle->ae_name_bad = 0; DPRINT(DEBUG_ACL, acl_debug_level, ("A ACL entry %s -> opmask %x\n", acle->ae_name, acle->ae_op_allowed)); } else { if (acle->ae_target) free(acle->ae_target); free(acle); acle = (aent_t *) NULL; } } else { if (acle->ae_target) free(acle->ae_target); free(acle); acle = (aent_t *) NULL; } if ( nmatch >= 4 ) { char *trailing; trailing = &acle_restrictions[strlen(acle_restrictions)-1]; while ( isspace((int) *trailing) ) trailing--; trailing[1] = '\0'; acle->ae_restriction_string = strdup(acle_restrictions); } else { acle->ae_restriction_string = (char *) NULL; } acle->ae_restriction_bad = 0; acle->ae_restrictions = (restriction_t *) NULL; } } DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_parse_line() = %x\n", (long) acle)); return(acle); } /* * kadm5int_acl_parse_restrictions() - Parse optional restrictions field * * Allowed restrictions are: * [+-]flagname (recognized by krb5_string_to_flags) * flag is forced to indicated value * -clearpolicy policy is forced clear * -policy pol policy is forced to be "pol" * -{expire,pwexpire,maxlife,maxrenewlife} deltat * associated value will be forced to * MIN(deltat, requested value) * * Returns: 0 on success, or system errors */ static krb5_error_code kadm5int_acl_parse_restrictions(s, rpp) char *s; restriction_t **rpp; { char *sp, *tp, *ap; static const char *delims = "\t\n\f\v\r ,"; krb5_deltat dt; krb5_flags flag; krb5_error_code code; DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_parse_restrictions(s=%20s, rpp=0x%08x)\n", s, (long)rpp)); *rpp = (restriction_t *) NULL; code = 0; if (s) { if (!(sp = strdup(s)) /* Don't munge the original */ || !(*rpp = (restriction_t *) malloc(sizeof(restriction_t)))) { code = ENOMEM; } else { memset(*rpp, 0, sizeof(**rpp)); for (tp=strtok(sp, delims); tp; tp=strtok((char *)NULL, delims)) { flag = 0; if (!krb5_string_to_flags(tp, "+", "-", &flag)) { /* OK, but was it in the positive or negative sense? */ if (flag) { (*rpp)->require_attrs |= flag; } else { flag = ~0; (void) krb5_string_to_flags(tp, "+", "-", &flag); (*rpp)->forbid_attrs |= ~flag; } (*rpp)->mask |= KADM5_ATTRIBUTES; } else if (!strcmp(tp, "-clearpolicy")) { (*rpp)->mask |= KADM5_POLICY_CLR; } else { /* everything else needs an argument ... */ if (!(ap = strtok((char *)NULL, delims))) { code = EINVAL; break; } if (!strcmp(tp, "-policy")) { if (!((*rpp)->policy = strdup(ap))) { code = ENOMEM; break; } (*rpp)->mask |= KADM5_POLICY; } else { /* all other arguments must be a deltat ... */ if (krb5_string_to_deltat(ap, &dt)) { code = EINVAL; break; } if (!strcmp(tp, "-expire")) { (*rpp)->princ_lifetime = dt; (*rpp)->mask |= KADM5_PRINC_EXPIRE_TIME; } else if (!strcmp(tp, "-pwexpire")) { (*rpp)->pw_lifetime = dt; (*rpp)->mask |= KADM5_PW_EXPIRATION; } else if (!strcmp(tp, "-maxlife")) { (*rpp)->max_life = dt; (*rpp)->mask |= KADM5_MAX_LIFE; } else if (!strcmp(tp, "-maxrenewlife")) { (*rpp)->max_renewable_life = dt; (*rpp)->mask |= KADM5_MAX_RLIFE; } else { code = EINVAL; break; } } } } } } if (sp) free(sp); if (*rpp && code) { if ((*rpp)->policy) free((*rpp)->policy); free(*rpp); *rpp = (restriction_t *) NULL; } DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_parse_restrictions() = %d, mask=0x%08x\n", code, (*rpp) ? (*rpp)->mask : 0)); return code; } /* * kadm5int_acl_impose_restrictions() - impose restrictions, modifying *recp, *maskp * * Returns: 0 on success; * malloc or timeofday errors */ krb5_error_code kadm5int_acl_impose_restrictions(kcontext, recp, maskp, rp) krb5_context kcontext; kadm5_principal_ent_rec *recp; long *maskp; restriction_t *rp; { krb5_error_code code; krb5_int32 now; DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_impose_restrictions(..., *maskp=0x%08x, rp=0x%08x)\n", *maskp, (long)rp)); if (!rp) return 0; if (rp->mask & (KADM5_PRINC_EXPIRE_TIME|KADM5_PW_EXPIRATION)) if ((code = krb5_timeofday(kcontext, &now))) return code; if (rp->mask & KADM5_ATTRIBUTES) { recp->attributes |= rp->require_attrs; recp->attributes &= ~(rp->forbid_attrs); *maskp |= KADM5_ATTRIBUTES; } if (rp->mask & KADM5_POLICY_CLR) { *maskp &= ~KADM5_POLICY; *maskp |= KADM5_POLICY_CLR; } else if (rp->mask & KADM5_POLICY) { if (recp->policy && strcmp(recp->policy, rp->policy)) { free(recp->policy); recp->policy = (char *) NULL; } if (!recp->policy) { recp->policy = strdup(rp->policy); /* XDR will free it */ if (!recp->policy) return ENOMEM; } *maskp |= KADM5_POLICY; } if (rp->mask & KADM5_PRINC_EXPIRE_TIME) { if (!(*maskp & KADM5_PRINC_EXPIRE_TIME) || (recp->princ_expire_time > (now + rp->princ_lifetime))) recp->princ_expire_time = now + rp->princ_lifetime; *maskp |= KADM5_PRINC_EXPIRE_TIME; } if (rp->mask & KADM5_PW_EXPIRATION) { if (!(*maskp & KADM5_PW_EXPIRATION) || (recp->pw_expiration > (now + rp->pw_lifetime))) recp->pw_expiration = now + rp->pw_lifetime; *maskp |= KADM5_PW_EXPIRATION; } if (rp->mask & KADM5_MAX_LIFE) { if (!(*maskp & KADM5_MAX_LIFE) || (recp->max_life > rp->max_life)) recp->max_life = rp->max_life; *maskp |= KADM5_MAX_LIFE; } if (rp->mask & KADM5_MAX_RLIFE) { if (!(*maskp & KADM5_MAX_RLIFE) || (recp->max_renewable_life > rp->max_renewable_life)) recp->max_renewable_life = rp->max_renewable_life; *maskp |= KADM5_MAX_RLIFE; } DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_impose_restrictions() = 0, *maskp=0x%08x\n", *maskp)); return 0; } /* * kadm5int_acl_free_entries() - Free all ACL entries. */ static void kadm5int_acl_free_entries() { aent_t *ap; aent_t *np; DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_free_entries()\n")); for (ap=acl_list_head; ap; ap = np) { if (ap->ae_name) free(ap->ae_name); if (ap->ae_principal) krb5_free_principal((krb5_context) NULL, ap->ae_principal); if (ap->ae_target) free(ap->ae_target); if (ap->ae_target_princ) krb5_free_principal((krb5_context) NULL, ap->ae_target_princ); if (ap->ae_restriction_string) free(ap->ae_restriction_string); if (ap->ae_restrictions) { if (ap->ae_restrictions->policy) free(ap->ae_restrictions->policy); free(ap->ae_restrictions); } np = ap->ae_next; free(ap); } acl_list_head = acl_list_tail = (aent_t *) NULL; acl_inited = 0; DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_free_entries()\n")); } /* * kadm5int_acl_load_acl_file() - Open and parse the ACL file. */ static int kadm5int_acl_load_acl_file() { FILE *afp; char *alinep; aent_t **aentpp; int alineno; int retval = 1; DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_load_acl_file()\n")); /* Open the ACL file for read */ afp = fopen(acl_acl_file, "rF"); /* Solaris Kerberos */ if (afp) { alineno = 1; aentpp = &acl_list_head; /* Get a non-comment line */ while ((alinep = kadm5int_acl_get_line(afp, &alineno))) { /* Parse it */ *aentpp = kadm5int_acl_parse_line(alinep); /* If syntax error, then fall out */ if (!*aentpp) { krb5_klog_syslog(LOG_ERR, ACL_SYN_ERR_MSG, acl_acl_file, alineno, alinep); retval = 0; break; } acl_list_tail = *aentpp; aentpp = &(*aentpp)->ae_next; } fclose(afp); if (acl_catchall_entry) { *aentpp = kadm5int_acl_parse_line(acl_catchall_entry); if (*aentpp) { acl_list_tail = *aentpp; } else { retval = 0; DPRINT(DEBUG_OPERATION, acl_debug_level, ("> catchall acl entry (%s) load failed\n", acl_catchall_entry)); } } } else { krb5_klog_syslog(LOG_ERR, ACL_CANTOPEN_MSG, error_message(errno), acl_acl_file); if (acl_catchall_entry && (acl_list_head = kadm5int_acl_parse_line((char *)acl_catchall_entry))) { acl_list_tail = acl_list_head; } else { retval = 0; DPRINT(DEBUG_OPERATION, acl_debug_level, ("> catchall acl entry (%s) load failed\n", acl_catchall_entry)); } } if (!retval) { kadm5int_acl_free_entries(); } DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_load_acl_file() = %d\n", retval)); return(retval); } /* * kadm5int_acl_match_data() - See if two data entries match. * * Wildcarding is only supported for a whole component. */ static krb5_boolean kadm5int_acl_match_data(e1, e2, targetflag, ws) krb5_data *e1, *e2; int targetflag; wildstate_t *ws; { krb5_boolean retval; DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_match_entry(%s, %s)\n", e1->data, e2->data)); retval = 0; if (!strncmp(e1->data, "*", e1->length)) { retval = 1; if (ws && !targetflag) { if (ws->nwild >= 9) { /* Solaris Kerberos */ DPRINT(DEBUG_ACL, acl_debug_level, ("Too many wildcards in ACL entry %s\n", e1->data)); } else ws->backref[ws->nwild++] = e2; } } else if (ws && targetflag && (e1->length == 2) && (e1->data[0] == '*') && (e1->data[1] >= '1') && (e1->data[1] <= '9')) { int n = e1->data[1] - '1'; if (n >= ws->nwild) { /* Solaris Kerberos */ DPRINT(DEBUG_ACL, acl_debug_level, ("Too many backrefs in ACL entry %s\n", e1->data)); } else if ((ws->backref[n]->length == e2->length) && (!strncmp(ws->backref[n]->data, e2->data, e2->length))) retval = 1; } else { if ((e1->length == e2->length) && (!strncmp(e1->data, e2->data, e1->length))) retval = 1; } DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_match_entry()=%d\n",retval)); return(retval); } /* * kadm5int_acl_find_entry() - Find a matching entry. */ static aent_t * kadm5int_acl_find_entry(kcontext, principal, dest_princ) krb5_context kcontext; krb5_principal principal; krb5_principal dest_princ; { aent_t *entry; krb5_error_code kret; int i; int matchgood; wildstate_t state; DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_find_entry()\n")); memset((char *)&state, 0, sizeof state); for (entry=acl_list_head; entry; entry = entry->ae_next) { if (entry->ae_name_bad) continue; if (!strcmp(entry->ae_name, "*")) { DPRINT(DEBUG_ACL, acl_debug_level, ("A wildcard ACL match\n")); matchgood = 1; } else { if (!entry->ae_principal && !entry->ae_name_bad) { kret = krb5_parse_name(kcontext, entry->ae_name, &entry->ae_principal); if (kret) entry->ae_name_bad = 1; } if (entry->ae_name_bad) { DPRINT(DEBUG_ACL, acl_debug_level, ("Bad ACL entry %s\n", entry->ae_name)); continue; } matchgood = 0; if (kadm5int_acl_match_data(&entry->ae_principal->realm, &principal->realm, 0, (wildstate_t *)0) && (entry->ae_principal->length == principal->length)) { matchgood = 1; for (i=0; ilength; i++) { if (!kadm5int_acl_match_data(&entry->ae_principal->data[i], &principal->data[i], 0, &state)) { matchgood = 0; break; } } } } if (!matchgood) continue; /* We've matched the principal. If we have a target, then try it */ if (entry->ae_target && strcmp(entry->ae_target, "*")) { if (!entry->ae_target_princ && !entry->ae_target_bad) { kret = krb5_parse_name(kcontext, entry->ae_target, &entry->ae_target_princ); if (kret) entry->ae_target_bad = 1; } if (entry->ae_target_bad) { DPRINT(DEBUG_ACL, acl_debug_level, ("Bad target in ACL entry for %s\n", entry->ae_name)); entry->ae_name_bad = 1; continue; } if (!dest_princ) matchgood = 0; else if (entry->ae_target_princ && dest_princ) { if (kadm5int_acl_match_data(&entry->ae_target_princ->realm, &dest_princ->realm, 1, (wildstate_t *)0) && (entry->ae_target_princ->length == dest_princ->length)) { for (i=0; ilength; i++) { if (!kadm5int_acl_match_data(&entry->ae_target_princ->data[i], &dest_princ->data[i], 1, &state)) { matchgood = 0; break; } } } else matchgood = 0; } } if (!matchgood) continue; if (entry->ae_restriction_string && !entry->ae_restriction_bad && !entry->ae_restrictions && kadm5int_acl_parse_restrictions(entry->ae_restriction_string, &entry->ae_restrictions)) { DPRINT(DEBUG_ACL, acl_debug_level, ("Bad restrictions in ACL entry for %s\n", entry->ae_name)); entry->ae_restriction_bad = 1; } if (entry->ae_restriction_bad) { entry->ae_name_bad = 1; continue; } break; } DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_find_entry()=%x\n",entry)); return(entry); } /* * kadm5int_acl_init() - Initialize ACL context. */ krb5_error_code kadm5int_acl_init(kcontext, debug_level, acl_file) krb5_context kcontext; int debug_level; char *acl_file; { krb5_error_code kret; kret = 0; acl_debug_level = debug_level; DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_init(afile=%s)\n", ((acl_file) ? acl_file : "(null)"))); acl_acl_file = (acl_file) ? acl_file : (char *) KRB5_DEFAULT_ADMIN_ACL; acl_inited = kadm5int_acl_load_acl_file(); DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_init() = %d\n", kret)); return(kret); } /* * kadm5int_acl_finish - Terminate ACL context. */ void kadm5int_acl_finish(kcontext, debug_level) krb5_context kcontext; int debug_level; { DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_finish()\n")); kadm5int_acl_free_entries(); DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_finish()\n")); } /* * kadm5int_acl_check() - Is this operation permitted for this principal? * this code used not to be based on gssapi. In order * to minimize porting hassles, I've put all the * gssapi hair in this function. This might not be * the best medium-term solution. (The best long-term * solution is, of course, a real authorization service.) */ krb5_boolean kadm5int_acl_check(kcontext, caller, opmask, principal, restrictions) krb5_context kcontext; gss_name_t caller; krb5_int32 opmask; krb5_principal principal; restriction_t **restrictions; { krb5_boolean retval; aent_t *aentry; gss_buffer_desc caller_buf; gss_OID caller_oid; OM_uint32 emaj, emin; krb5_error_code code; krb5_principal caller_princ; DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_op_permitted()\n")); /* Solaris Kerberos */ if (restrictions) *restrictions = NULL; if (GSS_ERROR(emaj = gss_display_name(&emin, caller, &caller_buf, &caller_oid))) return(1); code = krb5_parse_name(kcontext, (char *) caller_buf.value, &caller_princ); gss_release_buffer(&emin, &caller_buf); if (code) return(code); retval = 0; aentry = kadm5int_acl_find_entry(kcontext, caller_princ, principal); if (aentry) { if ((aentry->ae_op_allowed & opmask) == opmask) { retval = 1; if (restrictions) { *restrictions = (aentry->ae_restrictions && aentry->ae_restrictions->mask) ? aentry->ae_restrictions : (restriction_t *) NULL; } } } krb5_free_principal(kcontext, caller_princ); DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_op_permitted()=%d\n", retval)); return(retval); } kadm5_ret_t kadm5_get_privs(void *server_handle, long *privs) { CHECK_HANDLE(server_handle); /* this is impossible to do with the current interface. For now, return all privs, which will confuse some clients, but not deny any access to users of "smart" clients which try to cache */ *privs = ~0; return KADM5_OK; } /* SUNWresync121 (SEAM1.0) XXX */ kadm5_ret_t __kadm5_get_priv(void *server_handle, long *privs, gss_name_t client) { aent_t *aentry; gss_buffer_desc caller_buff; gss_OID caller_oid; krb5_principal caller_principal; OM_uint32 minor, major; krb5_error_code k_error; kadm5_ret_t retval = KADM5_FAILURE; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); if (GSS_ERROR(major = gss_display_name(&minor, client, &caller_buff, &caller_oid))) return(retval); k_error = krb5_parse_name(handle->context, (char *) caller_buff.value, &caller_principal); gss_release_buffer(&minor, &caller_buff); if (k_error) return(retval); if (aentry = kadm5int_acl_find_entry(handle->context, caller_principal, (krb5_principal)NULL)) *privs = aentry->ae_op_allowed; krb5_free_principal(handle->context, caller_principal); return (KADM5_OK); } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifdef __cplusplus extern "C" { #endif /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * kadmin/v5server/kadm5_defs.h * * Copyright 1995 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ #ifndef SERVER_ACL_H__ #define SERVER_ACL_H__ #include /* SUNWresync121 XXX */ /* * Debug definitions. */ #define DEBUG_SPROC 1 #define DEBUG_OPERATION 2 #define DEBUG_HOST 4 #define DEBUG_REALM 8 #define DEBUG_REQUESTS 16 #define DEBUG_ACL 32 #define DEBUG_PROTO 64 #define DEBUG_CALLS 128 #define DEBUG_NOSLAVES 256 #ifdef DEBUG #define DPRINT(l1, cl, al) if ((cl & l1) != 0) printf al #else /* DEBUG */ #define DPRINT(l1, cl, al) #endif /* DEBUG */ #define DLOG(l1, cl, msg) if ((cl & l1) != 0) \ com_err(programname, 0, msg) /* * Access control bits. */ #define ACL_INQUIRE 1 /* GET */ #define ACL_ADD 2 #define ACL_MODIFY 4 #define ACL_DELETE 8 #define ACL_LIST 16 #define ACL_CHANGEPW 32 /* #define ACL_CHANGE_OWN_PW 16 */ /* #define ACL_EXTRACT 64 */ #define ACL_SETKEY 256 #define ACL_MIGRATE 512 /* pam_krb5_migrate */ #define ACL_IPROP 65536 /* SUNW IProp */ #define ACL_RENAME (ACL_ADD+ACL_DELETE) #define ACL_ALL_MASK (ACL_ADD | \ ACL_DELETE | \ ACL_MODIFY | \ ACL_CHANGEPW | \ ACL_INQUIRE | \ ACL_LIST | \ ACL_IPROP | \ ACL_MIGRATE | \ ACL_SETKEY) typedef struct _restriction { long mask; krb5_flags require_attrs; krb5_flags forbid_attrs; krb5_deltat princ_lifetime; krb5_deltat pw_lifetime; krb5_deltat max_life; krb5_deltat max_renewable_life; long aux_attributes; char *policy; } restriction_t; krb5_error_code kadm5int_acl_init (krb5_context, int, char *); void kadm5int_acl_finish (krb5_context, int); krb5_boolean kadm5int_acl_check (krb5_context, gss_name_t, krb5_int32, krb5_principal, restriction_t **); krb5_error_code kadm5int_acl_impose_restrictions (krb5_context, kadm5_principal_ent_rec *, long *, restriction_t *); #endif /* SERVER_ACL_H__ */ #ifdef __cplusplus } #endif /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; #endif #include #include #include #include #include #include #include "server_internal.h" #include #include #include #include #ifdef HAVE_MEMORY_H #include #endif #include "adm_proto.h" #include #include static char **word_list = NULL; /* list of word pointers */ static char *word_block = NULL; /* actual word data */ static unsigned int word_count = 0; /* number of words */ /* * Function: word_compare * * Purpose: compare two words in the dictionary. * * Arguments: * w1 (input) pointer to first word * w2 (input) pointer to second word * result of strcmp * * Requires: * w1 and w2 to point to valid memory * */ static int word_compare(const void *s1, const void *s2) { return (strcasecmp(*(const char **)s1, *(const char **)s2)); } /* * Function: init-dict * * Purpose: Initialize in memory word dictionary * * Arguments: * none * KADM5_OK on success errno on failure; * (but success on ENOENT) * * Requires: * If WORDFILE exists, it must contain a list of words, * one word per-line. * * Effects: * If WORDFILE exists, it is read into memory sorted for future * use. If it does not exist, it syslogs an error message and returns * success. * * Modifies: * word_list to point to a chunck of allocated memory containing * pointers to words * word_block to contain the dictionary. * */ int init_dict(kadm5_config_params *params) { int fd, len, i; char *p, *t; struct stat sb; if(word_list != NULL && word_block != NULL) return KADM5_OK; if (! (params->mask & KADM5_CONFIG_DICT_FILE)) { /* Solaris Kerberos */ krb5_klog_syslog(LOG_INFO, dgettext(TEXT_DOMAIN, "No dictionary file specified, continuing " "without one.")); return KADM5_OK; } if ((fd = open(params->dict_file, O_RDONLY)) == -1) { if (errno == ENOENT) { /* Solaris Kerberos */ krb5_klog_syslog(LOG_ERR, dgettext(TEXT_DOMAIN, "WARNING! Cannot find dictionary file %s, " "continuing without one."), params->dict_file); return KADM5_OK; } else return errno; } if (fstat(fd, &sb) == -1) return errno; if ((word_block = (char *) malloc(sb.st_size + 1)) == NULL) return errno; if (read(fd, word_block, sb.st_size) != sb.st_size) return errno; (void) close(fd); word_block[sb.st_size] = '\0'; p = word_block; len = sb.st_size; while(len > 0 && (t = memchr(p, '\n', len)) != NULL) { *t = '\0'; len -= t - p + 1; p = t + 1; word_count++; } if ((word_list = (char **) malloc(word_count * sizeof(char *))) == NULL) return errno; p = word_block; for (i = 0; i < word_count; i++) { word_list[i] = p; p += strlen(p) + 1; } qsort(word_list, word_count, sizeof(char *), word_compare); return KADM5_OK; } /* * Function: find_word * * Purpose: See if the specified word exists in the in-core dictionary * * Arguments: * word (input) word to search for. * WORD_NOT_FOUND if not in dictionary, * KADM5_OK if if found word * errno if init needs to be called and returns an * error * * Requires: * word to be a null terminated string. * That word_list and word_block besetup * * Effects: * finds word in dictionary. * Modifies: * nothing. * */ int find_word(const char *word) { char **value; if(word_list == NULL || word_block == NULL) return WORD_NOT_FOUND; if ((value = (char **) bsearch(&word, word_list, word_count, sizeof(char *), word_compare)) == NULL) return WORD_NOT_FOUND; else return KADM5_OK; } /* * Function: destroy_dict * * Purpose: destroy in-core copy of dictionary. * * Arguments: * none * none * Requires: * nothing * Effects: * frees up memory occupied by word_list and word_block * sets count back to 0, and resets the pointers to NULL * * Modifies: * word_list, word_block, and word_count. * */ void destroy_dict(void) { if(word_list) { free(word_list); word_list = NULL; } if(word_block) { free(word_block); word_block = NULL; } if(word_count) word_count = 0; return; } /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ #include #include #include "server_internal.h" int _kadm5_check_handle(void *handle) { CHECK_HANDLE(handle); return 0; } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. * * $Id: server_init.c 18584 2006-09-13 20:30:23Z raeburn $ * $Source$ */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header: /cvs/krbdev/krb5/src/lib/kadm5/srv/server_init.c,v 1.8 2002/10/15 15:40:49 epeisach Exp $"; #endif #include #include #include #include #include "k5-int.h" /* needed for gssapiP_krb5.h */ #include #include #include "server_internal.h" #include /* * Function check_handle * * Purpose: Check a server handle and return a com_err code if it is * invalid or 0 if it is valid. * * Arguments: * * handle The server handle. */ static int check_handle(void *handle) { CHECK_HANDLE(handle); return 0; } static int dup_db_args(kadm5_server_handle_t handle, char **db_args) { int count = 0; int ret = 0; for (count=0; db_args && db_args[count]; count++); if (count == 0) { handle->db_args = NULL; goto clean_n_exit; } handle->db_args = calloc(sizeof(char*), count+1); if (handle->db_args == NULL) { ret=ENOMEM; goto clean_n_exit; } for (count=0; db_args[count]; count++) { handle->db_args[count] = strdup(db_args[count]); if (handle->db_args[count] == NULL) { ret = ENOMEM; goto clean_n_exit; } } clean_n_exit: if (ret && handle->db_args) { for (count=0; handle->db_args[count]; count++) free(handle->db_args[count]); free(handle->db_args), handle->db_args = NULL; } return ret; } static void free_db_args(kadm5_server_handle_t handle) { int count; if (handle->db_args) { for (count=0; handle->db_args[count]; count++) free(handle->db_args[count]); free(handle->db_args), handle->db_args = NULL; } } kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass, char *service_name, kadm5_config_params *params, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle) { return kadm5_init(client_name, pass, service_name, params, struct_version, api_version, db_args, server_handle); } kadm5_ret_t kadm5_init_with_creds(char *client_name, krb5_ccache ccache, char *service_name, kadm5_config_params *params, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle) { /* * A program calling init_with_creds *never* expects to prompt the * user. Therefore, always pass a dummy password in case this is * KADM5_API_VERSION_1. If this is KADM5_API_VERSION_2 and * MKEY_FROM_KBD is non-zero, return an error. */ if (api_version == KADM5_API_VERSION_2 && params && (params->mask & KADM5_CONFIG_MKEY_FROM_KBD) && params->mkey_from_kbd) return KADM5_BAD_SERVER_PARAMS; return kadm5_init(client_name, NULL, service_name, params, struct_version, api_version, db_args, server_handle); } kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab, char *service_name, kadm5_config_params *params, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle) { /* * A program calling init_with_skey *never* expects to prompt the * user. Therefore, always pass a dummy password in case this is * KADM5_API_VERSION_1. If this is KADM5_API_VERSION_2 and * MKEY_FROM_KBD is non-zero, return an error. */ if (api_version == KADM5_API_VERSION_2 && params && (params->mask & KADM5_CONFIG_MKEY_FROM_KBD) && params->mkey_from_kbd) return KADM5_BAD_SERVER_PARAMS; return kadm5_init(client_name, NULL, service_name, params, struct_version, api_version, db_args, server_handle); } /* * Solaris Kerberos: * A private extended version of kadm5_init which potentially * returns more information in case of an error. */ kadm5_ret_t kadm5_init2(char *client_name, char *pass, char *service_name, kadm5_config_params *params_in, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle, char **emsg) { int ret; kadm5_server_handle_t handle; kadm5_config_params params_local; /* for v1 compat */ if (emsg) *emsg = NULL; if (! server_handle) return EINVAL; if (! client_name) return EINVAL; if (! (handle = (kadm5_server_handle_t) malloc(sizeof *handle))) return ENOMEM; memset(handle, 0, sizeof(*handle)); ret = dup_db_args(handle, db_args); if (ret) { free(handle); return ret; } ret = (int) krb5int_init_context_kdc(&(handle->context)); if (ret) { free_db_args(handle); free(handle); return(ret); } handle->magic_number = KADM5_SERVER_HANDLE_MAGIC; handle->struct_version = struct_version; handle->api_version = api_version; /* * Verify the version numbers before proceeding; we can't use * CHECK_HANDLE because not all fields are set yet. */ GENERIC_CHECK_HANDLE(handle, KADM5_OLD_SERVER_API_VERSION, KADM5_NEW_SERVER_API_VERSION); /* * Acquire relevant profile entries. In version 2, merge values * in params_in with values from profile, based on * params_in->mask. * * In version 1, we've given a realm (which may be NULL) instead * of params_in. So use that realm, make params_in contain an * empty mask, and behave like version 2. */ memset((char *) ¶ms_local, 0, sizeof(params_local)); if (api_version == KADM5_API_VERSION_1) { params_local.realm = (char *) params_in; if (params_in) params_local.mask = KADM5_CONFIG_REALM; params_in = ¶ms_local; } #if 0 /* Now that we look at krb5.conf as well as kdc.conf, we can expect to see admin_server being set sometimes. */ #define ILLEGAL_PARAMS (KADM5_CONFIG_ADMIN_SERVER) if (params_in && (params_in->mask & ILLEGAL_PARAMS)) { krb5_free_context(handle->context); free_db_args(handle); free(handle); return KADM5_BAD_SERVER_PARAMS; } #endif ret = kadm5_get_config_params(handle->context, 1, params_in, &handle->params); if (ret) { krb5_free_context(handle->context); free_db_args(handle); free(handle); return(ret); } #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_DBNAME | \ KADM5_CONFIG_ADBNAME | \ KADM5_CONFIG_ADB_LOCKFILE | \ KADM5_CONFIG_ENCTYPE | \ KADM5_CONFIG_FLAGS | \ KADM5_CONFIG_MAX_LIFE | KADM5_CONFIG_MAX_RLIFE | \ KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_ENCTYPES) if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { kadm5_free_config_params(handle->context, &handle->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) { kadm5_free_config_params(handle->context, &handle->params); krb5_free_context(handle->context); free_db_args(handle); free(handle); return ret; } ret = krb5_db_open(handle->context, db_args, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN); if (ret) { if (emsg) { const char *m = krb5_get_error_message(handle->context, ret); *emsg = strdup(m); krb5_free_error_message(handle->context, m); } kadm5_free_config_params(handle->context, &handle->params); krb5_free_context(handle->context); free_db_args(handle); free(handle); return(ret); } if ((ret = krb5_parse_name(handle->context, client_name, &handle->current_caller))) { krb5_db_fini(handle->context); kadm5_free_config_params(handle->context, &handle->params); krb5_free_context(handle->context); free_db_args(handle); free(handle); return ret; } if (! (handle->lhandle = malloc(sizeof(*handle)))) { krb5_db_fini(handle->context); kadm5_free_config_params(handle->context, &handle->params); krb5_free_context(handle->context); free_db_args(handle); free(handle); return ENOMEM; } *handle->lhandle = *handle; handle->lhandle->api_version = KADM5_API_VERSION_2; handle->lhandle->struct_version = KADM5_STRUCT_VERSION; handle->lhandle->lhandle = handle->lhandle; /* can't check the handle until current_caller is set */ ret = check_handle((void *) handle); if (ret) { krb5_db_fini(handle->context); kadm5_free_config_params(handle->context, &handle->params); krb5_free_context(handle->context); free_db_args(handle); free(handle); return ret; } /* * The KADM5_API_VERSION_1 spec said "If pass (or keytab) is NULL * or an empty string, reads the master password from [the stash * file]. Otherwise, the non-NULL password is ignored and the * user is prompted for it via the tty." However, the code was * implemented the other way: when a non-NULL password was * provided, the stash file was used. This is somewhat more * sensible, as then a local or remote client that provides a * password does not prompt the user. This code maintains the * previous actual behavior, and not the old spec behavior, * because that is how the unit tests are written. * * In KADM5_API_VERSION_2, this decision is controlled by * params. * * kdb_init_master's third argument is "from_keyboard". */ /* * Solaris Kerberos: Setting to an unknown enc type will make the function * read the encryption type in the stash file instead of assumming that it * is the default type. */ if (handle->params.enctype == DEFAULT_KDC_ENCTYPE) handle->params.enctype = ENCTYPE_UNKNOWN; ret = kdb_init_master(handle, handle->params.realm, (handle->api_version == KADM5_API_VERSION_1 ? ((pass == NULL) || !(strlen(pass))) : ((handle->params.mask & KADM5_CONFIG_MKEY_FROM_KBD) && handle->params.mkey_from_kbd) )); if (ret) { krb5_db_fini(handle->context); kadm5_free_config_params(handle->context, &handle->params); krb5_free_context(handle->context); free_db_args(handle); free(handle); return ret; } /* * Solaris Kerberos: We used the enc type that was discovered in the stash * file to associate with the other magic principals in the database. */ handle->params.enctype = handle->master_keyblock.enctype; ret = kdb_init_hist(handle, handle->params.realm); if (ret) { krb5_db_fini(handle->context); kadm5_free_config_params(handle->context, &handle->params); krb5_free_context(handle->context); free_db_args(handle); free(handle); return ret; } ret = init_dict(&handle->params); if (ret) { krb5_db_fini(handle->context); krb5_free_principal(handle->context, handle->current_caller); kadm5_free_config_params(handle->context, &handle->params); krb5_free_context(handle->context); free_db_args(handle); free(handle); return ret; } *server_handle = (void *) handle; return KADM5_OK; } kadm5_ret_t kadm5_init(char *client_name, char *pass, char *service_name, kadm5_config_params *params_in, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle) { return (kadm5_init2(client_name, pass, service_name, params_in, struct_version, api_version, db_args, server_handle, NULL)); } kadm5_ret_t kadm5_destroy(void *server_handle) { kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); destroy_dict(); adb_policy_close(handle); krb5_db_fini(handle->context); krb5_free_principal(handle->context, handle->current_caller); kadm5_free_config_params(handle->context, &handle->params); krb5_free_context(handle->context); handle->magic_number = 0; free(handle->lhandle); free_db_args(handle); free(handle); return KADM5_OK; } kadm5_ret_t kadm5_lock(void *server_handle) { kadm5_server_handle_t handle = server_handle; kadm5_ret_t ret; CHECK_HANDLE(server_handle); ret = krb5_db_lock(handle->context, KRB5_DB_LOCKMODE_EXCLUSIVE); if (ret) return ret; return KADM5_OK; } kadm5_ret_t kadm5_unlock(void *server_handle) { kadm5_server_handle_t handle = server_handle; kadm5_ret_t ret; CHECK_HANDLE(server_handle); ret = krb5_db_unlock(handle->context); if (ret) return ret; return KADM5_OK; } kadm5_ret_t kadm5_flush(void *server_handle) { kadm5_server_handle_t handle = server_handle; kadm5_ret_t ret; CHECK_HANDLE(server_handle); if ((ret = krb5_db_fini(handle->context)) || (ret = krb5_db_open(handle->context, handle->db_args, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN)) || (ret = adb_policy_close(handle)) || (ret = adb_policy_init(handle))) { (void) kadm5_destroy(server_handle); return ret; } return KADM5_OK; } int _kadm5_check_handle(void *handle) { CHECK_HANDLE(handle); return 0; } #include "gssapiP_krb5.h" krb5_error_code kadm5_init_krb5_context (krb5_context *ctx) { /* Solaris Kerberos: not needed */ #if 0 /************** Begin IFDEF'ed OUT *******************************/ static int first_time = 1; if (first_time) { krb5_error_code err; err = krb5_gss_use_kdc_context(); if (err) return err; first_time = 0; } #endif /**************** END IFDEF'ed OUT *******************************/ return krb5int_init_context_kdc(ctx); } krb5_error_code kadm5_init_iprop(void *handle) { 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, FKCOMMAND)) != 0) return (retval); } return (0); } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; #endif #include #include #include "k5-int.h" #include #include "server_internal.h" extern caddr_t xdralloc_getdata(XDR *xdrs); extern void xdralloc_create(XDR *xdrs, enum xdr_op op); krb5_principal master_princ; krb5_db_entry master_db; krb5_principal hist_princ; krb5_keyblock hist_key; krb5_db_entry hist_db; krb5_kvno hist_kvno; /* much of this code is stolen from the kdc. there should be some library code to deal with this. */ krb5_error_code kdb_init_master(kadm5_server_handle_t handle, char *r, int from_keyboard) { int ret = 0; char *realm; krb5_boolean from_kbd = FALSE; if (from_keyboard) from_kbd = TRUE; if (r == NULL) { if ((ret = krb5_get_default_realm(handle->context, &realm))) return ret; } else { realm = r; } if ((ret = krb5_db_setup_mkey_name(handle->context, handle->params.mkey_name, realm, NULL, &master_princ))) goto done; /* Solaris Kerberos */ #if 0 master_keyblock.enctype = handle->params.enctype; #endif /* Solaris Kerberos */ ret = krb5_db_fetch_mkey(handle->context, master_princ, handle->params.enctype, from_kbd, FALSE /* only prompt once */, handle->params.stash_file, NULL /* I'm not sure about this, but it's what the kdc does --marc */, &handle->master_keyblock); if (ret) goto done; /* Solaris Kerberos */ if ((ret = krb5_db_verify_master_key(handle->context, master_princ, &handle->master_keyblock))) { krb5_db_fini(handle->context); return ret; } done: if (r == NULL) free(realm); return(ret); } /* * Function: kdb_init_hist * * Purpose: Initializes the global history variables. * * Arguments: * * handle (r) kadm5 api server handle * r (r) realm of history principal to use, or NULL * * Effects: This function sets the value of the following global * variables: * * hist_princ krb5_principal holding the history principal * hist_db krb5_db_entry of the history principal * hist_key krb5_keyblock holding the history principal's key * hist_encblock krb5_encrypt_block holding the procssed hist_key * hist_kvno the version number of the history key * * If the history principal does not already exist, this function * attempts to create it with kadm5_create_principal. WARNING! * If the history principal is deleted and this function is executed * (by kadmind, or kadmin.local, or anything else with permission), * the principal will be assigned a new random key and all existing * password history information will become useless. */ krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r) { int ret = 0; char *realm, *hist_name; krb5_key_data *key_data; krb5_key_salt_tuple ks[1]; if (r == NULL) { if ((ret = krb5_get_default_realm(handle->context, &realm))) return ret; } else { realm = r; } if ((hist_name = (char *) malloc(strlen(KADM5_HIST_PRINCIPAL) + strlen(realm) + 2)) == NULL) goto done; (void) sprintf(hist_name, "%s@%s", KADM5_HIST_PRINCIPAL, realm); if ((ret = krb5_parse_name(handle->context, hist_name, &hist_princ))) goto done; if ((ret = kdb_get_entry(handle, hist_princ, &hist_db, NULL))) { kadm5_principal_ent_rec ent; if (ret != KADM5_UNK_PRINC) goto done; /* try to create the principal */ memset(&ent, 0, sizeof(ent)); ent.principal = hist_princ; ent.max_life = KRB5_KDB_DISALLOW_ALL_TIX; ent.attributes = 0; /* this uses hist_kvno. So we set it to 2, which will be the correct value once the principal is created and randomized. Of course, it doesn't make sense to keep a history for the history principal, anyway. */ hist_kvno = 2; ks[0].ks_enctype = handle->params.enctype; ks[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL; ret = kadm5_create_principal_3(handle, &ent, (KADM5_PRINCIPAL | KADM5_MAX_LIFE | KADM5_ATTRIBUTES), 1, ks, "to-be-random"); if (ret) goto done; /* this won't let us randomize the hist_princ. So we cheat. */ hist_princ = NULL; ret = kadm5_randkey_principal_3(handle, ent.principal, 0, 1, ks, NULL, NULL); hist_princ = ent.principal; if (ret) goto done; /* now read the newly-created kdb record out of the database. */ if ((ret = kdb_get_entry(handle, hist_princ, &hist_db, NULL))) goto done; } ret = krb5_dbe_find_enctype(handle->context, &hist_db, handle->params.enctype, -1, -1, &key_data); if (ret) goto done; /* Solaris Kerberos */ ret = krb5_dbekd_decrypt_key_data(handle->context, &handle->master_keyblock, key_data, &hist_key, NULL); if (ret) goto done; hist_kvno = key_data->key_data_kvno; done: free(hist_name); if (r == NULL) free(realm); return ret; } /* * Function: kdb_get_entry * * Purpose: Gets an entry from the kerberos database and breaks * it out into a krb5_db_entry and an osa_princ_ent_t. * * Arguments: * * handle (r) the server_handle * principal (r) the principal to get * kdb (w) krb5_db_entry to fill in * adb (w) osa_princ_ent_rec to fill in * * when the caller is done with kdb and adb, kdb_free_entry must be * called to release them. The adb record is filled in with the * contents of the KRB5_TL_KADM_DATA record; if that record doesn't * exist, an empty but valid adb record is returned. */ krb5_error_code kdb_get_entry(kadm5_server_handle_t handle, krb5_principal principal, krb5_db_entry *kdb, osa_princ_ent_rec *adb) { krb5_error_code ret; int nprincs; krb5_boolean more; krb5_tl_data tl_data; XDR xdrs; ret = krb5_db_get_principal(handle->context, principal, kdb, &nprincs, &more); if (ret) return(ret); if (more) { krb5_db_free_principal(handle->context, kdb, nprincs); return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE); } else if (nprincs != 1) { krb5_db_free_principal(handle->context, kdb, nprincs); return(KADM5_UNK_PRINC); } if (adb) { memset(adb, 0, sizeof(*adb)); tl_data.tl_data_type = KRB5_TL_KADM_DATA; /* * XXX Currently, lookup_tl_data always returns zero; it sets * tl_data->tl_data_length to zero if the type isn't found. * This should be fixed... */ if ((ret = krb5_dbe_lookup_tl_data(handle->context, kdb, &tl_data)) || (tl_data.tl_data_length == 0)) { /* there's no admin data. this can happen, if the admin server is put into production after some principals are created. In this case, return valid admin data (which is all zeros with the hist_kvno filled in), and when the entry is written, the admin data will get stored correctly. */ adb->admin_history_kvno = hist_kvno; return(ret); } /* Solaris Kerberos */ xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents, tl_data.tl_data_length, XDR_DECODE); if (! xdr_osa_princ_ent_rec(&xdrs, adb)) { xdr_destroy(&xdrs); krb5_db_free_principal(handle->context, kdb, 1); return(KADM5_XDR_FAILURE); } xdr_destroy(&xdrs); } return(0); } /* * Function: kdb_free_entry * * Purpose: frees the resources allocated by kdb_get_entry * * Arguments: * * handle (r) the server_handle * kdb (w) krb5_db_entry to fill in * adb (w) osa_princ_ent_rec to fill in * * when the caller is done with kdb and adb, kdb_free_entry must be * called to release them. */ krb5_error_code kdb_free_entry(kadm5_server_handle_t handle, krb5_db_entry *kdb, osa_princ_ent_rec *adb) { XDR xdrs; if (kdb) krb5_db_free_principal(handle->context, kdb, 1); if (adb) { xdrmem_create(&xdrs, NULL, 0, XDR_FREE); xdr_osa_princ_ent_rec(&xdrs, adb); xdr_destroy(&xdrs); } return(0); } /* * Function: kdb_put_entry * * Purpose: Stores the osa_princ_ent_t and krb5_db_entry into to * database. * * Arguments: * * handle (r) the server_handle * kdb (r/w) the krb5_db_entry to store * adb (r) the osa_princ_db_ent to store * * Effects: * * The last modifier field of the kdb is set to the caller at now. * adb is encoded with xdr_osa_princ_ent_ret and stored in kbd as * KRB5_TL_KADM_DATA. kdb is then written to the database. */ krb5_error_code kdb_put_entry(kadm5_server_handle_t handle, krb5_db_entry *kdb, osa_princ_ent_rec *adb) { krb5_error_code ret; krb5_int32 now; XDR xdrs; krb5_tl_data tl_data; int one; ret = krb5_timeofday(handle->context, &now); if (ret) return(ret); ret = krb5_dbe_update_mod_princ_data(handle->context, kdb, now, handle->current_caller); if (ret) return(ret); xdralloc_create(&xdrs, XDR_ENCODE); if(! xdr_osa_princ_ent_rec(&xdrs, adb)) { xdr_destroy(&xdrs); return(KADM5_XDR_FAILURE); } tl_data.tl_data_type = KRB5_TL_KADM_DATA; tl_data.tl_data_length = xdr_getpos(&xdrs); /* Solaris Kerberos */ tl_data.tl_data_contents = (unsigned char *) xdralloc_getdata(&xdrs); ret = krb5_dbe_update_tl_data(handle->context, kdb, &tl_data); xdr_destroy(&xdrs); if (ret) return(ret); one = 1; ret = krb5_db_put_principal(handle->context, kdb, &one); if (ret) return(ret); return(0); } krb5_error_code kdb_delete_entry(kadm5_server_handle_t handle, krb5_principal name) { int one = 1; krb5_error_code ret; ret = krb5_db_delete_principal(handle->context, name, &one); return ret; } typedef struct _iter_data { void (*func)(void *, krb5_principal); void *data; } iter_data; static krb5_error_code kdb_iter_func(krb5_pointer data, krb5_db_entry *kdb) { iter_data *id = (iter_data *) data; (*(id->func))(id->data, kdb->princ); return(0); } krb5_error_code kdb_iter_entry(kadm5_server_handle_t handle, char *match_entry, void (*iter_fct)(void *, krb5_principal), void *data) { iter_data id; krb5_error_code ret; id.func = iter_fct; id.data = data; /* Solaris Kerberos: added support for db_args */ ret = krb5_db_iterate(handle->context, match_entry, kdb_iter_func, &id, NULL); if (ret) return(ret); return(0); } /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; #endif #include "k5-int.h" #include #include #include /* for strcasecmp */ #include #include "server_internal.h" kadm5_ret_t adb_policy_init(kadm5_server_handle_t handle) { /* now policy is initialized as part of database. No seperate call needed */ /* Solaris Kerberos: krb5_db_inited returns 0 when db has been inited */ if( krb5_db_inited( handle->context ) == 0 ) return KADM5_OK; return krb5_db_open( handle->context, NULL, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN ); } kadm5_ret_t adb_policy_close(kadm5_server_handle_t handle) { /* will be taken care by database close */ return KADM5_OK; } #ifdef HESIOD /* stolen from v4sever/kadm_funcs.c */ static char * reverse(str) char *str; { static char newstr[80]; char *p, *q; int i; i = strlen(str); if (i >= sizeof(newstr)) i = sizeof(newstr)-1; p = str+i-1; q = newstr; q[i]='\0'; for(; i > 0; i--) *q++ = *p--; return(newstr); } #endif /* HESIOD */ #if 0 static int lower(str) char *str; { register char *cp; int effect=0; for (cp = str; *cp; cp++) { if (isupper(*cp)) { *cp = tolower(*cp); effect++; } } return(effect); } #endif #ifdef HESIOD static int str_check_gecos(gecos, pwstr) char *gecos; char *pwstr; { char *cp, *ncp, *tcp; for (cp = gecos; *cp; ) { /* Skip past punctuation */ for (; *cp; cp++) if (isalnum(*cp)) break; /* Skip to the end of the word */ for (ncp = cp; *ncp; ncp++) if (!isalnum(*ncp) && *ncp != '\'') break; /* Delimit end of word */ if (*ncp) *ncp++ = '\0'; /* Check word to see if it's the password */ if (*cp) { if (!strcasecmp(pwstr, cp)) return 1; tcp = reverse(cp); if (!strcasecmp(pwstr, tcp)) return 1; cp = ncp; } else break; } return 0; } #endif /* HESIOD */ /* some of this is stolen from gatekeeper ... */ kadm5_ret_t passwd_check(kadm5_server_handle_t handle, char *password, int use_policy, kadm5_policy_ent_t pol, krb5_principal principal) { int nupper = 0, nlower = 0, ndigit = 0, npunct = 0, nspec = 0; char c, *s, *cp; #ifdef HESIOD extern struct passwd *hes_getpwnam(); struct passwd *ent; #endif if(use_policy) { if(strlen(password) < pol->pw_min_length) return KADM5_PASS_Q_TOOSHORT; s = password; while ((c = *s++)) { if (islower((unsigned char) c)) { nlower = 1; continue; } else if (isupper((unsigned char) c)) { nupper = 1; continue; } else if (isdigit((unsigned char) c)) { ndigit = 1; continue; } else if (ispunct((unsigned char) c)) { npunct = 1; continue; } else { nspec = 1; continue; } } if ((nupper + nlower + ndigit + npunct + nspec) < pol->pw_min_classes) return KADM5_PASS_Q_CLASS; if((find_word(password) == KADM5_OK)) return KADM5_PASS_Q_DICT; else { int i, n = krb5_princ_size(handle->context, principal); cp = krb5_princ_realm(handle->context, principal)->data; if (strcasecmp(cp, password) == 0) return KADM5_PASS_Q_DICT; for (i = 0; i < n ; i++) { cp = krb5_princ_component(handle->context, principal, i)->data; if (strcasecmp(cp, password) == 0) return KADM5_PASS_Q_DICT; #ifdef HESIOD ent = hes_getpwnam(cp); if (ent && ent->pw_gecos) if (str_check_gecos(ent->pw_gecos, password)) return KADM5_PASS_Q_DICT; /* XXX new error code? */ #endif } return KADM5_OK; } } else { if (strlen(password) < 1) return KADM5_PASS_Q_TOOSHORT; } return KADM5_OK; } void trunc_name(size_t *len, char **dots) { *dots = *len > MAXPRINCLEN ? "..." : ""; *len = *len > MAXPRINCLEN ? MAXPRINCLEN : *len; } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ #include "server_internal.h" #include kadm5_ret_t kadm5_chpass_principal_util(void *server_handle, krb5_principal princ, char *new_pw, char **ret_pw, char *msg_ret, unsigned int msg_len) { kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); return _kadm5_chpass_principal_util(handle, handle->lhandle, princ, new_pw, ret_pw, msg_ret, msg_len); } kadm5_ret_t kadm5_chpass_principal_v2(void *server_handle, krb5_principal princ, char *password, kadm5_ret_t *srvr_rsp_code, krb5_data *srvr_msg) { /* This method of password changing is not supported by the server */ return (KADM5_FAILURE); } krb5_chgpwd_prot _kadm5_get_kpasswd_protocol(void *handle) { /* * This has to be here because the higher level doesnt know * the details of the handle structure */ kadm5_server_handle_t srvrhdl = (kadm5_server_handle_t)handle; return (srvrhdl->params.kpasswd_protocol); } /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; #endif #include "autoconf.h" #if defined(HAVE_COMPILE) && defined(HAVE_STEP) #define SOLARIS_REGEXPS #elif defined(HAVE_REGCOMP) && defined(HAVE_REGEXEC) #define POSIX_REGEXPS #elif defined(HAVE_RE_COMP) && defined(HAVE_RE_EXEC) #define BSD_REGEXPS #else #error I cannot find any regexp functions #endif #include #include #include "server_internal.h" #include #ifdef SOLARIS_REGEXPS #include #endif #ifdef POSIX_REGEXPS #include #endif #include struct iter_data { krb5_context context; char **names; int n_names, sz_names; unsigned int malloc_failed; char *exp; #ifdef SOLARIS_REGEXPS char *expbuf; #endif #ifdef POSIX_REGEXPS regex_t preg; #endif }; /* * Function: glob_to_regexp * * Arguments: * * glob (r) the shell-style glob (?*[]) to convert * realm (r) the default realm to append, or NULL * regexp (w) the ed-style regexp created from glob * * Effects: * * regexp is filled in with allocated memory contained a regular * expression to be used with re_comp/compile that matches what the * shell-style glob would match. If glob does not contain an "@" * character and realm is not NULL, "@*" is appended to the regexp. * * Conversion algorithm: * * quoted characters are copied quoted * ? is converted to . * * is converted to .* * active characters are quoted: ^, $, . * [ and ] are active but supported and have the same meaning, so * they are copied * other characters are copied * regexp is anchored with ^ and $ */ static kadm5_ret_t glob_to_regexp(char *glob, char *realm, char **regexp) { int append_realm; char *p; /* validate the glob */ if (glob[strlen(glob)-1] == '\\') return EINVAL; /* A character of glob can turn into two in regexp, plus ^ and $ */ /* and trailing null. If glob has no @, also allocate space for */ /* the realm. */ append_realm = (realm != NULL) && (strchr(glob, '@') == NULL); p = (char *) malloc(strlen(glob)*2+ 3 + (append_realm ? 2 : 0)); if (p == NULL) return ENOMEM; *regexp = p; *p++ = '^'; while (*glob) { switch (*glob) { case '?': *p++ = '.'; break; case '*': *p++ = '.'; *p++ = '*'; break; case '.': case '^': case '$': *p++ = '\\'; *p++ = *glob; break; case '\\': *p++ = '\\'; *p++ = *++glob; break; default: *p++ = *glob; break; } glob++; } if (append_realm) { *p++ = '@'; *p++ = '*'; } *p++ = '$'; *p++ = '\0'; return KADM5_OK; } static void get_either_iter(struct iter_data *data, char *name) { int match; #ifdef SOLARIS_REGEXPS match = (step(name, data->expbuf) != 0); #endif #ifdef POSIX_REGEXPS match = (regexec(&data->preg, name, 0, NULL, 0) == 0); #endif #ifdef BSD_REGEXPS match = (re_exec(name) != 0); #endif if (match) { if (data->n_names == data->sz_names) { int new_sz = data->sz_names * 2; char **new_names = realloc(data->names, new_sz * sizeof(char *)); if (new_names) { data->names = new_names; data->sz_names = new_sz; } else { data->malloc_failed = 1; free(name); return; } } data->names[data->n_names++] = name; } else free(name); } static void get_pols_iter(void *data, osa_policy_ent_t entry) { char *name; if ((name = strdup(entry->name)) == NULL) return; get_either_iter(data, name); } static void get_princs_iter(void *data, krb5_principal princ) { struct iter_data *id = (struct iter_data *) data; char *name; if (krb5_unparse_name(id->context, princ, &name) != 0) return; get_either_iter(data, name); } static kadm5_ret_t kadm5_get_either(int princ, void *server_handle, char *exp, char ***princs, int *count) { struct iter_data data; #ifdef BSD_REGEXPS char *msg; #endif char *regexp; int i, ret; kadm5_server_handle_t handle = server_handle; *count = 0; if (exp == NULL) exp = "*"; CHECK_HANDLE(server_handle); if ((ret = glob_to_regexp(exp, princ ? handle->params.realm : NULL, ®exp)) != KADM5_OK) return ret; if ( #ifdef SOLARIS_REGEXPS ((data.expbuf = compile(regexp, NULL, NULL)) == NULL) #endif #ifdef POSIX_REGEXPS ((regcomp(&data.preg, regexp, REG_NOSUB)) != 0) #endif #ifdef BSD_REGEXPS ((msg = (char *) re_comp(regexp)) != NULL) #endif ) { /* XXX syslog msg or regerr(regerrno) */ free(regexp); return EINVAL; } data.n_names = 0; data.sz_names = 10; data.malloc_failed = 0; data.names = malloc(sizeof(char *) * data.sz_names); if (data.names == NULL) { free(regexp); return ENOMEM; } if (princ) { data.context = handle->context; ret = kdb_iter_entry(handle, exp, get_princs_iter, (void *) &data); } else { ret = krb5_db_iter_policy(handle->context, exp, get_pols_iter, (void *)&data); } free(regexp); #ifdef POSIX_REGEXPS regfree(&data.preg); #endif if ( !ret && data.malloc_failed) ret = ENOMEM; if ( ret ) { for (i = 0; i < data.n_names; i++) free(data.names[i]); free(data.names); return ret; } *princs = data.names; *count = data.n_names; return KADM5_OK; } kadm5_ret_t kadm5_get_principals(void *server_handle, char *exp, char ***princs, int *count) { return kadm5_get_either(1, server_handle, exp, princs, count); } kadm5_ret_t kadm5_get_policies(void *server_handle, char *exp, char ***pols, int *count) { return kadm5_get_either(0, server_handle, exp, pols, count); } /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ * */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; #endif #include #include #include "server_internal.h" kadm5_ret_t kadm5_free_principal_ent(void *server_handle, kadm5_principal_ent_t val) { kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); if(val) { if(val->principal) krb5_free_principal(handle->context, val->principal); if(val->mod_name) krb5_free_principal(handle->context, val->mod_name); if(val->policy) free(val->policy); /* XXX free key_data and tl_data */ if (handle->api_version == KADM5_API_VERSION_1) free(val); } return KADM5_OK; } /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; #endif #include "server_internal.h" #include #include #include #include #define MAX_PW_HISTORY 10 #define MIN_PW_HISTORY 1 #define MIN_PW_CLASSES 1 #define MAX_PW_CLASSES 5 #define MIN_PW_LENGTH 1 /* * Function: kadm5_create_policy * * Purpose: Create Policies in the policy DB. * * Arguments: * entry (input) The policy entry to be written out to the DB. * mask (input) Specifies which fields in entry are to ge written out * and which get default values. * 0 if successful otherwise an error code is returned. * * Requires: * Entry must be a valid principal entry, and mask have a valid value. * * Effects: * Verifies that mask does not specify that the refcount should * be set as part of the creation, and calls * kadm5_create_policy_internal. If the refcount *is* * specified, returns KADM5_BAD_MASK. */ kadm5_ret_t kadm5_create_policy(void *server_handle, kadm5_policy_ent_t entry, long mask) { CHECK_HANDLE(server_handle); krb5_clear_error_message(((kadm5_server_handle_t)server_handle)->context); if (mask & KADM5_REF_COUNT) return KADM5_BAD_MASK; else return kadm5_create_policy_internal(server_handle, entry, mask); } /* * Function: kadm5_create_policy_internal * * Purpose: Create Policies in the policy DB. * * Arguments: * entry (input) The policy entry to be written out to the DB. * mask (input) Specifies which fields in entry are to ge written out * and which get default values. * 0 if successful otherwise an error code is returned. * * Requires: * Entry must be a valid principal entry, and mask have a valid value. * * Effects: * Writes the data to the database, and does a database sync if * successful. * */ kadm5_ret_t kadm5_create_policy_internal(void *server_handle, kadm5_policy_ent_t entry, long mask) { kadm5_server_handle_t handle = server_handle; osa_policy_ent_rec pent; int ret; char *p; CHECK_HANDLE(server_handle); if ((entry == (kadm5_policy_ent_t) NULL) || (entry->policy == NULL)) return EINVAL; if(strlen(entry->policy) == 0) return KADM5_BAD_POLICY; if (!(mask & KADM5_POLICY)) return KADM5_BAD_MASK; pent.name = entry->policy; p = entry->policy; while(*p != '\0') { if(*p < ' ' || *p > '~') return KADM5_BAD_POLICY; else p++; } if (!(mask & KADM5_PW_MAX_LIFE)) pent.pw_max_life = 0; else pent.pw_max_life = entry->pw_max_life; if (!(mask & KADM5_PW_MIN_LIFE)) pent.pw_min_life = 0; else { if((mask & KADM5_PW_MAX_LIFE)) { if(entry->pw_min_life > entry->pw_max_life && entry->pw_max_life != 0) return KADM5_BAD_MIN_PASS_LIFE; } pent.pw_min_life = entry->pw_min_life; } if (!(mask & KADM5_PW_MIN_LENGTH)) pent.pw_min_length = MIN_PW_LENGTH; else { if(entry->pw_min_length < MIN_PW_LENGTH) return KADM5_BAD_LENGTH; pent.pw_min_length = entry->pw_min_length; } if (!(mask & KADM5_PW_MIN_CLASSES)) pent.pw_min_classes = MIN_PW_CLASSES; else { if(entry->pw_min_classes > MAX_PW_CLASSES || entry->pw_min_classes < MIN_PW_CLASSES) return KADM5_BAD_CLASS; pent.pw_min_classes = entry->pw_min_classes; } if (!(mask & KADM5_PW_HISTORY_NUM)) pent.pw_history_num = MIN_PW_HISTORY; else { if(entry->pw_history_num < MIN_PW_HISTORY || entry->pw_history_num > MAX_PW_HISTORY) return KADM5_BAD_HISTORY; else pent.pw_history_num = entry->pw_history_num; } if (!(mask & KADM5_REF_COUNT)) pent.policy_refcnt = 0; else pent.policy_refcnt = entry->policy_refcnt; if ((ret = krb5_db_create_policy(handle->context, &pent))) return ret; else return KADM5_OK; } kadm5_ret_t kadm5_delete_policy(void *server_handle, kadm5_policy_t name) { kadm5_server_handle_t handle = server_handle; osa_policy_ent_t entry; int ret; int cnt=1; CHECK_HANDLE(server_handle); krb5_clear_error_message(handle->context); if(name == (kadm5_policy_t) NULL) return EINVAL; if(strlen(name) == 0) return KADM5_BAD_POLICY; if((ret = krb5_db_get_policy(handle->context, name, &entry,&cnt))) return ret; if( cnt != 1 ) return KADM5_UNK_POLICY; if(entry->policy_refcnt != 0) { krb5_db_free_policy(handle->context, entry); return KADM5_POLICY_REF; } krb5_db_free_policy(handle->context, entry); if ((ret = krb5_db_delete_policy(handle->context, name))) return ret; else return KADM5_OK; } kadm5_ret_t kadm5_modify_policy(void *server_handle, kadm5_policy_ent_t entry, long mask) { CHECK_HANDLE(server_handle); krb5_clear_error_message(((kadm5_server_handle_t)server_handle)->context); if (mask & KADM5_REF_COUNT) return KADM5_BAD_MASK; else return kadm5_modify_policy_internal(server_handle, entry, mask); } kadm5_ret_t kadm5_modify_policy_internal(void *server_handle, kadm5_policy_ent_t entry, long mask) { kadm5_server_handle_t handle = server_handle; osa_policy_ent_t p; int ret; int cnt=1; CHECK_HANDLE(server_handle); if((entry == (kadm5_policy_ent_t) NULL) || (entry->policy == NULL)) return EINVAL; if(strlen(entry->policy) == 0) return KADM5_BAD_POLICY; if((mask & KADM5_POLICY)) return KADM5_BAD_MASK; if ((ret = krb5_db_get_policy(handle->context, entry->policy, &p, &cnt))) return ret; if (cnt != 1) return KADM5_UNK_POLICY; if ((mask & KADM5_PW_MAX_LIFE)) p->pw_max_life = entry->pw_max_life; if ((mask & KADM5_PW_MIN_LIFE)) { if(entry->pw_min_life > p->pw_max_life && p->pw_max_life != 0) { krb5_db_free_policy(handle->context, p); return KADM5_BAD_MIN_PASS_LIFE; } p->pw_min_life = entry->pw_min_life; } if ((mask & KADM5_PW_MIN_LENGTH)) { if(entry->pw_min_length < MIN_PW_LENGTH) { krb5_db_free_policy(handle->context, p); return KADM5_BAD_LENGTH; } p->pw_min_length = entry->pw_min_length; } if ((mask & KADM5_PW_MIN_CLASSES)) { if(entry->pw_min_classes > MAX_PW_CLASSES || entry->pw_min_classes < MIN_PW_CLASSES) { krb5_db_free_policy(handle->context, p); return KADM5_BAD_CLASS; } p->pw_min_classes = entry->pw_min_classes; } if ((mask & KADM5_PW_HISTORY_NUM)) { if(entry->pw_history_num < MIN_PW_HISTORY || entry->pw_history_num > MAX_PW_HISTORY) { krb5_db_free_policy(handle->context, p); return KADM5_BAD_HISTORY; } p->pw_history_num = entry->pw_history_num; } if ((mask & KADM5_REF_COUNT)) p->policy_refcnt = entry->policy_refcnt; ret = krb5_db_put_policy(handle->context, p); krb5_db_free_policy(handle->context, p); return ret; } kadm5_ret_t kadm5_get_policy(void *server_handle, kadm5_policy_t name, kadm5_policy_ent_t entry) { osa_policy_ent_t t; kadm5_policy_ent_rec entry_local, **entry_orig, *new; int ret; kadm5_server_handle_t handle = server_handle; int cnt=1; CHECK_HANDLE(server_handle); krb5_clear_error_message(handle->context); /* * In version 1, entry is a pointer to a kadm5_policy_ent_t that * should be filled with allocated memory. */ if (handle->api_version == KADM5_API_VERSION_1) { entry_orig = (kadm5_policy_ent_rec **) entry; *entry_orig = NULL; entry = &entry_local; } if (name == (kadm5_policy_t) NULL) return EINVAL; if(strlen(name) == 0) return KADM5_BAD_POLICY; if((ret = krb5_db_get_policy(handle->context, name, &t, &cnt))) return ret; if( cnt != 1 ) return KADM5_UNK_POLICY; if ((entry->policy = (char *) malloc(strlen(t->name) + 1)) == NULL) { krb5_db_free_policy(handle->context, t); return ENOMEM; } strcpy(entry->policy, t->name); entry->pw_min_life = t->pw_min_life; entry->pw_max_life = t->pw_max_life; entry->pw_min_length = t->pw_min_length; entry->pw_min_classes = t->pw_min_classes; entry->pw_history_num = t->pw_history_num; entry->policy_refcnt = t->policy_refcnt; krb5_db_free_policy(handle->context, t); if (handle->api_version == KADM5_API_VERSION_1) { new = (kadm5_policy_ent_t) malloc(sizeof(kadm5_policy_ent_rec)); if (new == NULL) { free(entry->policy); krb5_db_free_policy(handle->context, t); return ENOMEM; } *new = *entry; *entry_orig = new; } return KADM5_OK; } /* * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved */ #include #include #include #include "server_internal.h" #include #include #include #include #include #include #include #include #include #ifdef USE_PASSWORD_SERVER #include #endif extern krb5_principal master_princ; extern krb5_principal hist_princ; extern krb5_keyblock hist_key; extern krb5_db_entry master_db; extern krb5_db_entry hist_db; extern krb5_kvno hist_kvno; static int decrypt_key_data(krb5_context context, krb5_keyblock *, int n_key_data, krb5_key_data *key_data, krb5_keyblock **keyblocks, int *n_keys); static krb5_error_code kadm5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_principal *outprinc) { register krb5_principal tempprinc; register int i, nelems; tempprinc = (krb5_principal)krb5_db_alloc(context, NULL, sizeof(krb5_principal_data)); if (tempprinc == 0) return ENOMEM; memcpy(tempprinc, inprinc, sizeof(krb5_principal_data)); nelems = (int) krb5_princ_size(context, inprinc); tempprinc->data = krb5_db_alloc(context, NULL, nelems * sizeof(krb5_data)); if (tempprinc->data == 0) { krb5_db_free(context, (char *)tempprinc); return ENOMEM; } for (i = 0; i < nelems; i++) { unsigned int len = krb5_princ_component(context, inprinc, i)->length; krb5_princ_component(context, tempprinc, i)->length = len; if (((krb5_princ_component(context, tempprinc, i)->data = krb5_db_alloc(context, NULL, len)) == 0) && len) { while (--i >= 0) krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data); krb5_db_free (context, tempprinc->data); krb5_db_free (context, tempprinc); return ENOMEM; } if (len) memcpy(krb5_princ_component(context, tempprinc, i)->data, krb5_princ_component(context, inprinc, i)->data, len); } tempprinc->realm.data = krb5_db_alloc(context, NULL, tempprinc->realm.length = inprinc->realm.length); if (!tempprinc->realm.data && tempprinc->realm.length) { for (i = 0; i < nelems; i++) krb5_db_free(context, krb5_princ_component(context, tempprinc, i)->data); krb5_db_free(context, tempprinc->data); krb5_db_free(context, tempprinc); return ENOMEM; } if (tempprinc->realm.length) memcpy(tempprinc->realm.data, inprinc->realm.data, inprinc->realm.length); *outprinc = tempprinc; return 0; } static void kadm5_free_principal(krb5_context context, krb5_principal val) { register krb5_int32 i; if (!val) return; if (val->data) { i = krb5_princ_size(context, val); while(--i >= 0) krb5_db_free(context, krb5_princ_component(context, val, i)->data); krb5_db_free(context, val->data); } if (val->realm.data) krb5_db_free(context, val->realm.data); krb5_db_free(context, val); } /* * XXX Functions that ought to be in libkrb5.a, but aren't. */ kadm5_ret_t krb5_copy_key_data_contents(context, from, to) krb5_context context; krb5_key_data *from, *to; { int i, idx; *to = *from; idx = (from->key_data_ver == 1 ? 1 : 2); for (i = 0; i < idx; i++) { if ( from->key_data_length[i] ) { to->key_data_contents[i] = malloc(from->key_data_length[i]); if (to->key_data_contents[i] == NULL) { for (i = 0; i < idx; i++) { if (to->key_data_contents[i]) { memset(to->key_data_contents[i], 0, to->key_data_length[i]); free(to->key_data_contents[i]); } } return ENOMEM; } memcpy(to->key_data_contents[i], from->key_data_contents[i], from->key_data_length[i]); } } return 0; } static krb5_tl_data *dup_tl_data(krb5_tl_data *tl) { krb5_tl_data *n; n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)); if (n == NULL) return NULL; n->tl_data_contents = malloc(tl->tl_data_length); if (n->tl_data_contents == NULL) { free(n); return NULL; } memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length); n->tl_data_type = tl->tl_data_type; n->tl_data_length = tl->tl_data_length; n->tl_data_next = NULL; return n; } /* This is in lib/kdb/kdb_cpw.c, but is static */ static void cleanup_key_data(context, count, data) krb5_context context; int count; krb5_key_data * data; { int i, j; for (i = 0; i < count; i++) for (j = 0; j < data[i].key_data_ver; j++) if (data[i].key_data_length[j]) krb5_db_free(context, data[i].key_data_contents[j]); krb5_db_free(context, data); } kadm5_ret_t kadm5_create_principal(void *server_handle, kadm5_principal_ent_t entry, long mask, char *password) { return kadm5_create_principal_3(server_handle, entry, mask, 0, NULL, password); } kadm5_ret_t kadm5_create_principal_3(void *server_handle, kadm5_principal_ent_t entry, long mask, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char *password) { krb5_db_entry kdb; osa_princ_ent_rec adb; kadm5_policy_ent_rec polent; krb5_int32 now; krb5_tl_data *tl_data_tail; unsigned int ret; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); krb5_clear_error_message(handle->context); /* * Argument sanity checking, and opening up the DB */ if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) || (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) || (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) || (mask & KADM5_FAIL_AUTH_COUNT)) return KADM5_BAD_MASK; if((mask & ~ALL_PRINC_MASK)) return KADM5_BAD_MASK; if (entry == (kadm5_principal_ent_t) NULL || password == NULL) return EINVAL; /* * Check to see if the principal exists */ ret = kdb_get_entry(handle, entry->principal, &kdb, &adb); switch(ret) { case KADM5_UNK_PRINC: /* Solaris Kerberos */ memset(&kdb, 0, sizeof(krb5_db_entry)); memset(&adb, 0, sizeof(osa_princ_ent_rec)); break; case 0: /* * Solaris Kerberos: this allows an addprinc to be done on a mix-in * princ which has no keys initially. */ if (kdb.n_key_data != 0) { /* have a princ with keys, return dupe princ error */ kdb_free_entry(handle, &kdb, &adb); return KADM5_DUP; } else { /* * have a princ with no keys, let's replace it. Note, want to * keep the existing kdb tl_data (specifically the LDAP plugin * adds the DN to the tl_data which is needed to locate the dir. * entry). */ kdb_free_entry(handle, NULL, &adb); memset(&adb, 0, sizeof(osa_princ_ent_rec)); } break; default: return ret; } /* * If a policy was specified, load it. * If we can not find the one specified return an error */ if ((mask & KADM5_POLICY)) { if ((ret = kadm5_get_policy(handle->lhandle, entry->policy, &polent)) != KADM5_OK) { if(ret == EINVAL) return KADM5_BAD_POLICY; else return ret; } } if ((ret = passwd_check(handle, password, (mask & KADM5_POLICY), &polent, entry->principal))) { if (mask & KADM5_POLICY) (void) kadm5_free_policy_ent(handle->lhandle, &polent); return ret; } /* * Start populating the various DB fields, using the * "defaults" for fields that were not specified by the * mask. */ if ((ret = krb5_timeofday(handle->context, &now))) { if (mask & KADM5_POLICY) (void) kadm5_free_policy_ent(handle->lhandle, &polent); return ret; } kdb.magic = KRB5_KDB_MAGIC_NUMBER; kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */ /* * Solaris Kerberos: * If KADM5_ATTRIBUTES is set, we want to rope in not only * entry->attributes, but also the generic params.flags * obtained previously via kadm5_get_config_params. */ if ((mask & KADM5_ATTRIBUTES)) { kdb.attributes = handle->params.flags; kdb.attributes |= entry->attributes; } else { kdb.attributes = handle->params.flags; } if ((mask & KADM5_MAX_LIFE)) kdb.max_life = entry->max_life; else kdb.max_life = handle->params.max_life; if (mask & KADM5_MAX_RLIFE) kdb.max_renewable_life = entry->max_renewable_life; else kdb.max_renewable_life = handle->params.max_rlife; if ((mask & KADM5_PRINC_EXPIRE_TIME)) kdb.expiration = entry->princ_expire_time; else kdb.expiration = handle->params.expiration; kdb.pw_expiration = 0; if ((mask & KADM5_POLICY)) { if(polent.pw_max_life) kdb.pw_expiration = now + polent.pw_max_life; else kdb.pw_expiration = 0; } if ((mask & KADM5_PW_EXPIRATION)) kdb.pw_expiration = entry->pw_expiration; kdb.last_success = 0; kdb.last_failed = 0; kdb.fail_auth_count = 0; /* this is kind of gross, but in order to free the tl data, I need to free the entire kdb entry, and that will try to free the principal. */ if ((ret = kadm5_copy_principal(handle->context, entry->principal, &(kdb.princ)))) { if (mask & KADM5_POLICY) (void) kadm5_free_policy_ent(handle->lhandle, &polent); return(ret); } if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))) { krb5_db_free_principal(handle->context, &kdb, 1); if (mask & KADM5_POLICY) (void) kadm5_free_policy_ent(handle->lhandle, &polent); return(ret); } if (mask & KADM5_TL_DATA) { /* splice entry->tl_data onto the front of kdb.tl_data */ for (tl_data_tail = entry->tl_data; tl_data_tail; tl_data_tail = tl_data_tail->tl_data_next) { ret = krb5_dbe_update_tl_data(handle->context, &kdb, tl_data_tail); if( ret ) { krb5_db_free_principal(handle->context, &kdb, 1); if (mask & KADM5_POLICY) (void) kadm5_free_policy_ent(handle->lhandle, &polent); return ret; } } } /* initialize the keys */ if ((ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock, n_ks_tuple?ks_tuple:handle->params.keysalts, n_ks_tuple?n_ks_tuple:handle->params.num_keysalts, password, (mask & KADM5_KVNO)?entry->kvno:1, FALSE, &kdb))) { krb5_db_free_principal(handle->context, &kdb, 1); if (mask & KADM5_POLICY) (void) kadm5_free_policy_ent(handle->lhandle, &polent); return(ret); } /* populate the admin-server-specific fields. In the OV server, this used to be in a separate database. Since there's already marshalling code for the admin fields, to keep things simple, I'm going to keep it, and make all the admin stuff occupy a single tl_data record, */ adb.admin_history_kvno = hist_kvno; if ((mask & KADM5_POLICY)) { adb.aux_attributes = KADM5_POLICY; /* this does *not* need to be strdup'ed, because adb is xdr */ /* encoded in osa_adb_create_princ, and not ever freed */ adb.policy = entry->policy; } /* increment the policy ref count, if any */ if ((mask & KADM5_POLICY)) { polent.policy_refcnt++; if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent, KADM5_REF_COUNT)) != KADM5_OK) { krb5_db_free_principal(handle->context, &kdb, 1); if (mask & KADM5_POLICY) (void) kadm5_free_policy_ent(handle->lhandle, &polent); return(ret); } } /* In all cases key and the principal data is set, let the database provider know */ kdb.mask = mask | KADM5_KEY_DATA | KADM5_PRINCIPAL ; /* store the new db entry */ ret = kdb_put_entry(handle, &kdb, &adb); krb5_db_free_principal(handle->context, &kdb, 1); if (ret) { if ((mask & KADM5_POLICY)) { /* decrement the policy ref count */ polent.policy_refcnt--; /* * if this fails, there's nothing we can do anyway. the * policy refcount wil be too high. */ (void) kadm5_modify_policy_internal(handle->lhandle, &polent, KADM5_REF_COUNT); } if (mask & KADM5_POLICY) (void) kadm5_free_policy_ent(handle->lhandle, &polent); return(ret); } if (mask & KADM5_POLICY) (void) kadm5_free_policy_ent(handle->lhandle, &polent); return KADM5_OK; } kadm5_ret_t kadm5_delete_principal(void *server_handle, krb5_principal principal) { unsigned int ret; kadm5_policy_ent_rec polent; krb5_db_entry kdb; osa_princ_ent_rec adb; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); krb5_clear_error_message(handle->context); if (principal == NULL) return EINVAL; if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) return(ret); if ((adb.aux_attributes & KADM5_POLICY)) { if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &polent)) == KADM5_OK) { polent.policy_refcnt--; if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent, KADM5_REF_COUNT)) != KADM5_OK) { (void) kadm5_free_policy_ent(handle->lhandle, &polent); kdb_free_entry(handle, &kdb, &adb); return(ret); } } if ((ret = kadm5_free_policy_ent(handle->lhandle, &polent))) { kdb_free_entry(handle, &kdb, &adb); return ret; } } ret = kdb_delete_entry(handle, principal); kdb_free_entry(handle, &kdb, &adb); return ret; } kadm5_ret_t kadm5_modify_principal(void *server_handle, kadm5_principal_ent_t entry, long mask) { int ret, ret2, i; kadm5_policy_ent_rec npol, opol; int have_npol = 0, have_opol = 0; krb5_db_entry kdb; krb5_tl_data *tl_data_orig; osa_princ_ent_rec adb; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); krb5_clear_error_message(handle->context); if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) || (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) || (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED)) return KADM5_BAD_MASK; if((mask & ~ALL_PRINC_MASK)) return KADM5_BAD_MASK; if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR)) return KADM5_BAD_MASK; if(entry == (kadm5_principal_ent_t) NULL) return EINVAL; if (mask & KADM5_TL_DATA) { tl_data_orig = entry->tl_data; while (tl_data_orig) { if (tl_data_orig->tl_data_type < 256) return KADM5_BAD_TL_TYPE; tl_data_orig = tl_data_orig->tl_data_next; } } ret = kdb_get_entry(handle, entry->principal, &kdb, &adb); if (ret) return(ret); /* * This is pretty much the same as create ... */ if ((mask & KADM5_POLICY)) { /* get the new policy */ ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol); if (ret) { switch (ret) { case EINVAL: ret = KADM5_BAD_POLICY; break; case KADM5_UNK_POLICY: case KADM5_BAD_POLICY: ret = KADM5_UNK_POLICY; break; } goto done; } have_npol = 1; /* if we already have a policy, get it to decrement the refcnt */ if(adb.aux_attributes & KADM5_POLICY) { /* ... but not if the old and new are the same */ if(strcmp(adb.policy, entry->policy)) { ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol); switch(ret) { case EINVAL: case KADM5_BAD_POLICY: case KADM5_UNK_POLICY: break; case KADM5_OK: have_opol = 1; opol.policy_refcnt--; break; default: goto done; break; } npol.policy_refcnt++; } } else npol.policy_refcnt++; /* set us up to use the new policy */ adb.aux_attributes |= KADM5_POLICY; if (adb.policy) free(adb.policy); adb.policy = strdup(entry->policy); /* set pw_max_life based on new policy */ if (npol.pw_max_life) { ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb, &(kdb.pw_expiration)); if (ret) goto done; kdb.pw_expiration += npol.pw_max_life; } else { kdb.pw_expiration = 0; } } if ((mask & KADM5_POLICY_CLR) && (adb.aux_attributes & KADM5_POLICY)) { ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol); switch(ret) { case EINVAL: case KADM5_BAD_POLICY: case KADM5_UNK_POLICY: ret = KADM5_BAD_DB; goto done; break; case KADM5_OK: have_opol = 1; if (adb.policy) free(adb.policy); adb.policy = NULL; adb.aux_attributes &= ~KADM5_POLICY; kdb.pw_expiration = 0; opol.policy_refcnt--; break; default: goto done; break; } } if (((mask & KADM5_POLICY) || (mask & KADM5_POLICY_CLR)) && (((have_opol) && (ret = kadm5_modify_policy_internal(handle->lhandle, &opol, KADM5_REF_COUNT))) || ((have_npol) && (ret = kadm5_modify_policy_internal(handle->lhandle, &npol, KADM5_REF_COUNT))))) goto done; if ((mask & KADM5_ATTRIBUTES)) kdb.attributes = entry->attributes; if ((mask & KADM5_MAX_LIFE)) kdb.max_life = entry->max_life; if ((mask & KADM5_PRINC_EXPIRE_TIME)) kdb.expiration = entry->princ_expire_time; if (mask & KADM5_PW_EXPIRATION) kdb.pw_expiration = entry->pw_expiration; if (mask & KADM5_MAX_RLIFE) kdb.max_renewable_life = entry->max_renewable_life; if (mask & KADM5_FAIL_AUTH_COUNT) kdb.fail_auth_count = entry->fail_auth_count; if((mask & KADM5_KVNO)) { for (i = 0; i < kdb.n_key_data; i++) kdb.key_data[i].key_data_kvno = entry->kvno; } if (mask & KADM5_TL_DATA) { krb5_tl_data *tl; /* may have to change the version number of the API. Updates the list with the given tl_data rather than over-writting */ for (tl = entry->tl_data; tl; tl = tl->tl_data_next) { ret = krb5_dbe_update_tl_data(handle->context, &kdb, tl); if( ret ) { goto done; } } } /* let the mask propagate to the database provider */ kdb.mask = mask; ret = kdb_put_entry(handle, &kdb, &adb); if (ret) goto done; ret = KADM5_OK; done: if (have_opol) { ret2 = kadm5_free_policy_ent(handle->lhandle, &opol); ret = ret ? ret : ret2; } if (have_npol) { ret2 = kadm5_free_policy_ent(handle->lhandle, &npol); ret = ret ? ret : ret2; } kdb_free_entry(handle, &kdb, &adb); return ret; } kadm5_ret_t kadm5_rename_principal(void *server_handle, krb5_principal source, krb5_principal target) { krb5_db_entry kdb; osa_princ_ent_rec adb; int ret, i; kadm5_server_handle_t handle = server_handle; CHECK_HANDLE(server_handle); krb5_clear_error_message(handle->context); if (source == NULL || target == NULL) return EINVAL; if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) { kdb_free_entry(handle, &kdb, &adb); return(KADM5_DUP); } if ((ret = kdb_get_entry(handle, source, &kdb, &adb))) return ret; /* this is kinda gross, but unavoidable */ for (i=0; icontext, kdb.princ); ret = kadm5_copy_principal(handle->context, target, &kdb.princ); if (ret) { kdb.princ = NULL; /* so freeing the dbe doesn't lose */ goto done; } if ((ret = kdb_put_entry(handle, &kdb, &adb))) goto done; ret = kdb_delete_entry(handle, source); done: kdb_free_entry(handle, &kdb, &adb); return ret; } kadm5_ret_t kadm5_get_principal(void *server_handle, krb5_principal principal, kadm5_principal_ent_t entry, long in_mask) { krb5_db_entry kdb; osa_princ_ent_rec adb; krb5_error_code ret = 0; long mask; int i; kadm5_server_handle_t handle = server_handle; kadm5_principal_ent_rec entry_local, *entry_orig; CHECK_HANDLE(server_handle); krb5_clear_error_message(handle->context); /* * In version 1, all the defined fields are always returned. * entry is a pointer to a kadm5_principal_ent_t_v1 that should be * filled with allocated memory. */ if (handle->api_version == KADM5_API_VERSION_1) { mask = KADM5_PRINCIPAL_NORMAL_MASK; entry_orig = entry; entry = &entry_local; } else { mask = in_mask; } memset((char *) entry, 0, sizeof(*entry)); if (principal == NULL) return EINVAL; if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) return ret; if ((mask & KADM5_POLICY) && adb.policy && (adb.aux_attributes & KADM5_POLICY)) { if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) { ret = ENOMEM; goto done; } strcpy(entry->policy, adb.policy); } if (mask & KADM5_AUX_ATTRIBUTES) entry->aux_attributes = adb.aux_attributes; if ((mask & KADM5_PRINCIPAL) && (ret = krb5_copy_principal(handle->context, principal, &entry->principal))) { goto done; } if (mask & KADM5_PRINC_EXPIRE_TIME) entry->princ_expire_time = kdb.expiration; if ((mask & KADM5_LAST_PWD_CHANGE) && (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb, &(entry->last_pwd_change)))) { goto done; } if (mask & KADM5_PW_EXPIRATION) entry->pw_expiration = kdb.pw_expiration; if (mask & KADM5_MAX_LIFE) entry->max_life = kdb.max_life; /* this is a little non-sensical because the function returns two */ /* values that must be checked separately against the mask */ if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) { ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb, &(entry->mod_date), &(entry->mod_name)); if (ret) { goto done; } if (! (mask & KADM5_MOD_TIME)) entry->mod_date = 0; if (! (mask & KADM5_MOD_NAME)) { krb5_free_principal(handle->context, entry->principal); entry->principal = NULL; } } if (mask & KADM5_ATTRIBUTES) entry->attributes = kdb.attributes; if (mask & KADM5_KVNO) for (entry->kvno = 0, i=0; i entry->kvno) entry->kvno = kdb.key_data[i].key_data_kvno; if (handle->api_version == KADM5_API_VERSION_2) entry->mkvno = 0; else { /* XXX I'll be damned if I know how to deal with this one --marc */ entry->mkvno = 1; } /* * The new fields that only exist in version 2 start here */ if (handle->api_version == KADM5_API_VERSION_2) { if (mask & KADM5_MAX_RLIFE) entry->max_renewable_life = kdb.max_renewable_life; if (mask & KADM5_LAST_SUCCESS) entry->last_success = kdb.last_success; if (mask & KADM5_LAST_FAILED) entry->last_failed = kdb.last_failed; if (mask & KADM5_FAIL_AUTH_COUNT) entry->fail_auth_count = kdb.fail_auth_count; if (mask & KADM5_TL_DATA) { krb5_tl_data *tl, *tl2; entry->tl_data = NULL; tl = kdb.tl_data; while (tl) { if (tl->tl_data_type > 255) { if ((tl2 = dup_tl_data(tl)) == NULL) { ret = ENOMEM; goto done; } tl2->tl_data_next = entry->tl_data; entry->tl_data = tl2; entry->n_tl_data++; } tl = tl->tl_data_next; } } if (mask & KADM5_KEY_DATA) { entry->n_key_data = kdb.n_key_data; if(entry->n_key_data) { entry->key_data = (krb5_key_data *) malloc(entry->n_key_data*sizeof(krb5_key_data)); if (entry->key_data == NULL) { ret = ENOMEM; goto done; } } else entry->key_data = NULL; for (i = 0; i < entry->n_key_data; i++) ret = krb5_copy_key_data_contents(handle->context, &kdb.key_data[i], &entry->key_data[i]); if (ret) goto done; } } /* * If KADM5_API_VERSION_1, we return an allocated structure, and * we need to convert the new structure back into the format the * caller is expecting. */ if (handle->api_version == KADM5_API_VERSION_1) { kadm5_principal_ent_t_v1 newv1; newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1))); if (newv1 == NULL) { ret = ENOMEM; goto done; } newv1->principal = entry->principal; newv1->princ_expire_time = entry->princ_expire_time; newv1->last_pwd_change = entry->last_pwd_change; newv1->pw_expiration = entry->pw_expiration; newv1->max_life = entry->max_life; newv1->mod_name = entry->mod_name; newv1->mod_date = entry->mod_date; newv1->attributes = entry->attributes; newv1->kvno = entry->kvno; newv1->mkvno = entry->mkvno; newv1->policy = entry->policy; newv1->aux_attributes = entry->aux_attributes; *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1; } ret = KADM5_OK; done: if (ret && entry->principal) krb5_free_principal(handle->context, entry->principal); kdb_free_entry(handle, &kdb, &adb); return ret; } /* * Function: check_pw_reuse * * Purpose: Check if a key appears in a list of keys, in order to * enforce password history. * * Arguments: * * context (r) the krb5 context * hist_keyblock (r) the key that hist_key_data is * encrypted in * n_new_key_data (r) length of new_key_data * new_key_data (r) keys to check against * pw_hist_data, encrypted in hist_keyblock * n_pw_hist_data (r) length of pw_hist_data * pw_hist_data (r) passwords to check new_key_data against * * Effects: * For each new_key in new_key_data: * decrypt new_key with the master_keyblock * for each password in pw_hist_data: * for each hist_key in password: * decrypt hist_key with hist_keyblock * compare the new_key and hist_key * * Returns krb5 errors, KADM5_PASS_RESUSE if a key in * new_key_data is the same as a key in pw_hist_data, or 0. */ static kadm5_ret_t check_pw_reuse(krb5_context context, krb5_keyblock *master_keyblock, krb5_keyblock *hist_keyblock, int n_new_key_data, krb5_key_data *new_key_data, unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data) { int x, y, z; krb5_keyblock newkey, histkey; krb5_error_code ret; for (x = 0; x < n_new_key_data; x++) { ret = krb5_dbekd_decrypt_key_data(context, master_keyblock, &(new_key_data[x]), &newkey, NULL); if (ret) return(ret); for (y = 0; y < n_pw_hist_data; y++) { for (z = 0; z < pw_hist_data[y].n_key_data; z++) { ret = krb5_dbekd_decrypt_key_data(context, hist_keyblock, &pw_hist_data[y].key_data[z], &histkey, NULL); if (ret) return(ret); if ((newkey.length == histkey.length) && (newkey.enctype == histkey.enctype) && (memcmp(newkey.contents, histkey.contents, histkey.length) == 0)) { krb5_free_keyblock_contents(context, &histkey); krb5_free_keyblock_contents(context, &newkey); return(KADM5_PASS_REUSE); } krb5_free_keyblock_contents(context, &histkey); } } krb5_free_keyblock_contents(context, &newkey); } return(0); } /* * Function: create_history_entry * * Purpose: Creates a password history entry from an array of * key_data. * * Arguments: * * context (r) krb5_context to use * master_keyblcok (r) master key block * n_key_data (r) number of elements in key_data * key_data (r) keys to add to the history entry * hist (w) history entry to fill in * * Effects: * * hist->key_data is allocated to store n_key_data key_datas. Each * element of key_data is decrypted with master_keyblock, re-encrypted * in hist_key, and added to hist->key_data. hist->n_key_data is * set to n_key_data. */ static int create_history_entry(krb5_context context, krb5_keyblock *master_keyblock, int n_key_data, krb5_key_data *key_data, osa_pw_hist_ent *hist) { int i, ret; krb5_keyblock key; krb5_keysalt salt; hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data)); if (hist->key_data == NULL) return ENOMEM; memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data)); for (i = 0; i < n_key_data; i++) { ret = krb5_dbekd_decrypt_key_data(context, master_keyblock, &key_data[i], &key, &salt); if (ret) return ret; ret = krb5_dbekd_encrypt_key_data(context, &hist_key, &key, &salt, key_data[i].key_data_kvno, &hist->key_data[i]); if (ret) return ret; krb5_free_keyblock_contents(context, &key); /* krb5_free_keysalt(context, &salt); */ } hist->n_key_data = n_key_data; return 0; } static void free_history_entry(krb5_context context, osa_pw_hist_ent *hist) { int i; for (i = 0; i < hist->n_key_data; i++) krb5_free_key_data_contents(context, &hist->key_data[i]); free(hist->key_data); } /* * Function: add_to_history * * Purpose: Adds a password to a principal's password history. * * Arguments: * * context (r) krb5_context to use * adb (r/w) admin principal entry to add keys to * pol (r) adb's policy * pw (r) keys for the password to add to adb's key history * * Effects: * * add_to_history adds a single password to adb's password history. * pw contains n_key_data keys in its key_data, in storage should be * allocated but not freed by the caller (XXX blech!). * * This function maintains adb->old_keys as a circular queue. It * starts empty, and grows each time this function is called until it * is pol->pw_history_num items long. adb->old_key_len holds the * number of allocated entries in the array, and must therefore be [0, * pol->pw_history_num). adb->old_key_next is the index into the * array where the next element should be written, and must be [0, * adb->old_key_len). */ #define KADM_MOD(x) (x + adb->old_key_next) % adb->old_key_len static kadm5_ret_t add_to_history(krb5_context context, osa_princ_ent_t adb, kadm5_policy_ent_t pol, osa_pw_hist_ent *pw) { osa_pw_hist_ent *histp; uint32_t nhist; unsigned int i, knext, nkeys; nhist = pol->pw_history_num; /* A history of 1 means just check the current password */ if (nhist <= 1) return 0; nkeys = adb->old_key_len; knext = adb->old_key_next; /* resize the adb->old_keys array if necessary */ if (nkeys + 1 < nhist) { if (adb->old_keys == NULL) { adb->old_keys = (osa_pw_hist_ent *) malloc((nkeys + 1) * sizeof (osa_pw_hist_ent)); } else { adb->old_keys = (osa_pw_hist_ent *) realloc(adb->old_keys, (nkeys + 1) * sizeof (osa_pw_hist_ent)); } if (adb->old_keys == NULL) return(ENOMEM); memset(&adb->old_keys[nkeys], 0, sizeof(osa_pw_hist_ent)); nkeys = ++adb->old_key_len; /* * To avoid losing old keys, shift forward each entry after * knext. */ for (i = nkeys - 1; i > knext; i--) { adb->old_keys[i] = adb->old_keys[i - 1]; } memset(&adb->old_keys[knext], 0, sizeof(osa_pw_hist_ent)); } else if (nkeys + 1 > nhist) { /* * The policy must have changed! Shrink the array. * Can't simply realloc() down, since it might be wrapped. * To understand the arithmetic below, note that we are * copying into new positions 0 .. N-1 from old positions * old_key_next-N .. old_key_next-1, modulo old_key_len, * where N = pw_history_num - 1 is the length of the * shortened list. Matt Crawford, FNAL */ /* * M = adb->old_key_len, N = pol->pw_history_num - 1 * * tmp[0] .. tmp[N-1] = old[(knext-N)%M] .. old[(knext-1)%M] */ int j; osa_pw_hist_t tmp; tmp = (osa_pw_hist_ent *) malloc((nhist - 1) * sizeof (osa_pw_hist_ent)); if (tmp == NULL) return ENOMEM; for (i = 0; i < nhist - 1; i++) { /* * Add nkeys once before taking remainder to avoid * negative values. */ j = (i + nkeys + knext - (nhist - 1)) % nkeys; tmp[i] = adb->old_keys[j]; } /* Now free the ones we don't keep (the oldest ones) */ for (i = 0; i < nkeys - (nhist - 1); i++) { j = (i + nkeys + knext) % nkeys; histp = &adb->old_keys[j]; for (j = 0; j < histp->n_key_data; j++) { krb5_free_key_data_contents(context, &histp->key_data[j]); } free(histp->key_data); } free((void *)adb->old_keys); adb->old_keys = tmp; nkeys = adb->old_key_len = nhist - 1; knext = adb->old_key_next = 0; } /* * If nhist decreased since the last password change, and nkeys+1 * is less than the previous nhist, it is possible for knext to * index into unallocated space. This condition would not be * caught by the resizing code above. */ if (knext + 1 > nkeys) knext = adb->old_key_next = 0; /* free the old pw history entry if it contains data */ histp = &adb->old_keys[knext]; for (i = 0; i < histp->n_key_data; i++) krb5_free_key_data_contents(context, &histp->key_data[i]); free(histp->key_data); /* store the new entry */ adb->old_keys[knext] = *pw; /* update the next pointer */ if (++adb->old_key_next == nhist - 1) adb->old_key_next = 0; return(0); } #undef KADM_MOD #ifdef USE_PASSWORD_SERVER /* FIXME: don't use global variable for this */ krb5_boolean use_password_server = 0; static krb5_boolean kadm5_use_password_server (void) { return use_password_server; } void kadm5_set_use_password_server (void) { use_password_server = 1; } #endif #ifdef USE_PASSWORD_SERVER /* * kadm5_launch_task () runs a program (task_path) to synchronize the * Apple password server with the Kerberos database. Password server * programs can receive arguments on the command line (task_argv) * and a block of data via stdin (data_buffer). * * Because a failure to communicate with the tool results in the * password server falling out of sync with the database, * kadm5_launch_task() always fails if it can't talk to the tool. */ static kadm5_ret_t kadm5_launch_task (krb5_context context, const char *task_path, char * const task_argv[], const char *data_buffer) { kadm5_ret_t ret = 0; int data_pipe[2]; if (data_buffer != NULL) { ret = pipe (data_pipe); if (ret) { ret = errno; } } if (!ret) { pid_t pid = fork (); if (pid == -1) { ret = errno; } else if (pid == 0) { /* The child: */ if (data_buffer != NULL) { if (dup2 (data_pipe[0], STDIN_FILENO) == -1) { _exit (1); } } else { close (data_pipe[0]); } close (data_pipe[1]); execv (task_path, task_argv); _exit (1); /* Fail if execv fails */ } else { /* The parent: */ int status; if (data_buffer != NULL) { /* Write out the buffer to the child */ if (krb5_net_write (context, data_pipe[1], data_buffer, strlen (data_buffer)) < 0) { /* kill the child to make sure waitpid() won't hang later */ ret = errno; kill (pid, SIGKILL); } } close (data_buffer[0]); close (data_buffer[1]); waitpid (pid, &status, 0); if (!ret) { if (WIFEXITED (status)) { /* child read password and exited. Check the return value. */ if ((WEXITSTATUS (status) != 0) && (WEXITSTATUS (status) != 252)) { ret = KRB5KDC_ERR_POLICY; /* password change rejected */ } } else { /* child read password but crashed or was killed */ ret = KRB5KRB_ERR_GENERIC; /* FIXME: better error */ } } } } return ret; } #endif kadm5_ret_t kadm5_chpass_principal(void *server_handle, krb5_principal principal, char *password) { return kadm5_chpass_principal_3(server_handle, principal, FALSE, 0, NULL, password); } kadm5_ret_t kadm5_chpass_principal_3(void *server_handle, krb5_principal principal, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char *password) { krb5_int32 now; kadm5_policy_ent_rec pol; osa_princ_ent_rec adb; krb5_db_entry kdb, kdb_save; int ret, ret2, last_pwd, hist_added; int have_pol = 0; kadm5_server_handle_t handle = server_handle; osa_pw_hist_ent hist; CHECK_HANDLE(server_handle); /* Solaris Kerberos - kadm5_check_min_life checks for null principal. */ ret = kadm5_check_min_life(server_handle,principal,NULL,0); if (ret) return (ret); krb5_clear_error_message(handle->context); hist_added = 0; memset(&hist, 0, sizeof(hist)); if (principal == NULL || password == NULL) return EINVAL; if ((krb5_principal_compare(handle->context, principal, hist_princ)) == TRUE) return KADM5_PROTECT_PRINCIPAL; if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) return(ret); /* we are going to need the current keys after the new keys are set */ if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) { kdb_free_entry(handle, &kdb, &adb); return(ret); } if ((adb.aux_attributes & KADM5_POLICY)) { if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol))) goto done; have_pol = 1; } if ((ret = passwd_check(handle, password, adb.aux_attributes & KADM5_POLICY, &pol, principal))) goto done; ret = krb5_dbe_cpw(handle->context, &handle->master_keyblock, n_ks_tuple?ks_tuple:handle->params.keysalts, n_ks_tuple?n_ks_tuple:handle->params.num_keysalts, password, 0 /* increment kvno */, keepold, &kdb); if (ret) goto done; kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; ret = krb5_timeofday(handle->context, &now); if (ret) goto done; if ((adb.aux_attributes & KADM5_POLICY)) { /* the policy was loaded before */ ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb, &last_pwd); if (ret) goto done; #if 0 /* * The spec says this check is overridden if the caller has * modify privilege. The admin server therefore makes this * check itself (in chpass_principal_wrapper, misc.c). A * local caller implicitly has all authorization bits. */ if ((now - last_pwd) < pol.pw_min_life && !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { ret = KADM5_PASS_TOOSOON; goto done; } #endif ret = create_history_entry(handle->context, &handle->master_keyblock, kdb_save.n_key_data, kdb_save.key_data, &hist); if (ret) goto done; ret = check_pw_reuse(handle->context, &handle->master_keyblock, &hist_key, kdb.n_key_data, kdb.key_data, 1, &hist); if (ret) goto done; if (pol.pw_history_num > 1) { if (adb.admin_history_kvno != hist_kvno) { ret = KADM5_BAD_HIST_KEY; goto done; } ret = check_pw_reuse(handle->context, &handle->master_keyblock, &hist_key, kdb.n_key_data, kdb.key_data, adb.old_key_len, adb.old_keys); if (ret) goto done; ret = add_to_history(handle->context, &adb, &pol, &hist); if (ret) goto done; hist_added = 1; } if (pol.pw_max_life) kdb.pw_expiration = now + pol.pw_max_life; else kdb.pw_expiration = 0; } else { kdb.pw_expiration = 0; } #ifdef USE_PASSWORD_SERVER if (kadm5_use_password_server () && (krb5_princ_size (handle->context, principal) == 1)) { krb5_data *princ = krb5_princ_component (handle->context, principal, 0); const char *path = "/usr/sbin/mkpassdb"; char *argv[] = { "mkpassdb", "-setpassword", NULL, NULL }; char *pstring = NULL; char pwbuf[256]; int pwlen = strlen (password); if (pwlen > 254) pwlen = 254; strncpy (pwbuf, password, pwlen); pwbuf[pwlen] = '\n'; pwbuf[pwlen + 1] = '\0'; if (!ret) { pstring = malloc ((princ->length + 1) * sizeof (char)); if (pstring == NULL) { ret = errno; } } if (!ret) { memcpy (pstring, princ->data, princ->length); pstring [princ->length] = '\0'; argv[2] = pstring; ret = kadm5_launch_task (handle->context, path, argv, pwbuf); } if (pstring != NULL) free (pstring); if (ret) goto done; } #endif ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now); if (ret) goto done; /* key data and attributes changed, let the database provider know */ /* Solaris Kerberos: adding support for key history in LDAP KDB */ if (hist_added == 1) kdb.mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_KEY_HIST /* | KADM5_CPW_FUNCTION */; else kdb.mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES /* | KADM5_CPW_FUNCTION */; if ((ret = kdb_put_entry(handle, &kdb, &adb))) goto done; ret = KADM5_OK; done: if (!hist_added && hist.key_data) free_history_entry(handle->context, &hist); kdb_free_entry(handle, &kdb, &adb); kdb_free_entry(handle, &kdb_save, NULL); krb5_db_free_principal(handle->context, &kdb, 1); if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol)) && !ret) ret = ret2; return ret; } kadm5_ret_t kadm5_randkey_principal(void *server_handle, krb5_principal principal, krb5_keyblock **keyblocks, int *n_keys) { /* Solaris Kerberos: */ krb5_key_salt_tuple keysalts[2]; /* * Anyone calling this routine is forced to use only DES * enctypes to be compatible with earlier releases that * did not support stronger crypto. * * S10 (and later) kadmin clients will not use this API, * so we can assume the request is from an older version. */ keysalts[0].ks_enctype = ENCTYPE_DES_CBC_MD5; keysalts[0].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL; keysalts[1].ks_enctype = ENCTYPE_DES_CBC_CRC; keysalts[1].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL; return (kadm5_randkey_principal_3(server_handle, principal, FALSE, 2, keysalts, keyblocks, n_keys)); } kadm5_ret_t kadm5_randkey_principal_3(void *server_handle, krb5_principal principal, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, krb5_keyblock **keyblocks, int *n_keys) { krb5_db_entry kdb; osa_princ_ent_rec adb; krb5_int32 now; kadm5_policy_ent_rec pol; krb5_key_data *key_data; int ret, last_pwd, have_pol = 0; kadm5_server_handle_t handle = server_handle; if (keyblocks) *keyblocks = NULL; CHECK_HANDLE(server_handle); krb5_clear_error_message(handle->context); if (principal == NULL) return EINVAL; if (hist_princ && /* this will be NULL when initializing the databse */ ((krb5_principal_compare(handle->context, principal, hist_princ)) == TRUE)) return KADM5_PROTECT_PRINCIPAL; if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) return(ret); ret = krb5_dbe_crk(handle->context, &handle->master_keyblock, n_ks_tuple?ks_tuple:handle->params.keysalts, n_ks_tuple?n_ks_tuple:handle->params.num_keysalts, keepold, &kdb); if (ret) goto done; kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; ret = krb5_timeofday(handle->context, &now); if (ret) goto done; if ((adb.aux_attributes & KADM5_POLICY)) { if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)) != KADM5_OK) goto done; have_pol = 1; ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb, &last_pwd); if (ret) goto done; #if 0 /* * The spec says this check is overridden if the caller has * modify privilege. The admin server therefore makes this * check itself (in chpass_principal_wrapper, misc.c). A * local caller implicitly has all authorization bits. */ if((now - last_pwd) < pol.pw_min_life && !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { ret = KADM5_PASS_TOOSOON; goto done; } #endif if(pol.pw_history_num > 1) { if(adb.admin_history_kvno != hist_kvno) { ret = KADM5_BAD_HIST_KEY; goto done; } ret = check_pw_reuse(handle->context, &handle->master_keyblock, &hist_key, kdb.n_key_data, kdb.key_data, adb.old_key_len, adb.old_keys); if (ret) goto done; } if (pol.pw_max_life) kdb.pw_expiration = now + pol.pw_max_life; else kdb.pw_expiration = 0; } else { kdb.pw_expiration = 0; } ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now); if (ret) goto done; if (keyblocks) { if (handle->api_version == KADM5_API_VERSION_1) { /* Version 1 clients will expect to see a DES_CRC enctype. */ ret = krb5_dbe_find_enctype(handle->context, &kdb, ENCTYPE_DES_CBC_CRC, -1, -1, &key_data); if (ret) goto done; ret = decrypt_key_data(handle->context, &handle->master_keyblock, 1, key_data, keyblocks, NULL); if (ret) goto done; } else { ret = decrypt_key_data(handle->context, &handle->master_keyblock, kdb.n_key_data, kdb.key_data, keyblocks, n_keys); if (ret) goto done; } } /* key data changed, let the database provider know */ kdb.mask = KADM5_KEY_DATA /* | KADM5_RANDKEY_USED */; if ((ret = kdb_put_entry(handle, &kdb, &adb))) goto done; ret = KADM5_OK; done: kdb_free_entry(handle, &kdb, &adb); if (have_pol) kadm5_free_policy_ent(handle->lhandle, &pol); return ret; } #if 0 /* Solaris Kerberos */ /* * kadm5_setv4key_principal: * * Set only ONE key of the principal, removing all others. This key * must have the DES_CBC_CRC enctype and is entered as having the * krb4 salttype. This is to enable things like kadmind4 to work. */ kadm5_ret_t kadm5_setv4key_principal(void *server_handle, krb5_principal principal, krb5_keyblock *keyblock) { krb5_db_entry kdb; osa_princ_ent_rec adb; krb5_int32 now; kadm5_policy_ent_rec pol; krb5_keysalt keysalt; int i, k, kvno, ret, have_pol = 0; #if 0 int last_pwd; #endif kadm5_server_handle_t handle = server_handle; krb5_key_data tmp_key_data; memset( &tmp_key_data, 0, sizeof(tmp_key_data)); CHECK_HANDLE(server_handle); krb5_clear_error_message(handle->context); if (principal == NULL || keyblock == NULL) return EINVAL; if (hist_princ && /* this will be NULL when initializing the databse */ ((krb5_principal_compare(handle->context, principal, hist_princ)) == TRUE)) return KADM5_PROTECT_PRINCIPAL; if (keyblock->enctype != ENCTYPE_DES_CBC_CRC) return KADM5_SETV4KEY_INVAL_ENCTYPE; if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) return(ret); for (kvno = 0, i=0; i kvno) kvno = kdb.key_data[i].key_data_kvno; if (kdb.key_data != NULL) cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data); kdb.key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, sizeof(krb5_key_data)); if (kdb.key_data == NULL) return ENOMEM; memset(kdb.key_data, 0, sizeof(krb5_key_data)); kdb.n_key_data = 1; keysalt.type = KRB5_KDB_SALTTYPE_V4; /* XXX data.magic? */ keysalt.data.length = 0; keysalt.data.data = NULL; /* use tmp_key_data as temporary location and reallocate later */ ret = krb5_dbekd_encrypt_key_data(handle->context, &master_keyblock, keyblock, &keysalt, kvno + 1, &tmp_key_data); if (ret) { goto done; } for (k = 0; k < tmp_key_data.key_data_ver; k++) { kdb.key_data->key_data_type[k] = tmp_key_data.key_data_type[k]; kdb.key_data->key_data_length[k] = tmp_key_data.key_data_length[k]; if (tmp_key_data.key_data_contents[k]) { kdb.key_data->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]); if (kdb.key_data->key_data_contents[k] == NULL) { cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data); kdb.key_data = NULL; kdb.n_key_data = 0; ret = ENOMEM; goto done; } memcpy (kdb.key_data->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]); memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]); free (tmp_key_data.key_data_contents[k]); tmp_key_data.key_data_contents[k] = NULL; } } kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; ret = krb5_timeofday(handle->context, &now); if (ret) goto done; if ((adb.aux_attributes & KADM5_POLICY)) { if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)) != KADM5_OK) goto done; have_pol = 1; #if 0 /* * The spec says this check is overridden if the caller has * modify privilege. The admin server therefore makes this * check itself (in chpass_principal_wrapper, misc.c). A * local caller implicitly has all authorization bits. */ if (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb, &last_pwd)) goto done; if((now - last_pwd) < pol.pw_min_life && !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { ret = KADM5_PASS_TOOSOON; goto done; } #endif #if 0 /* * Should we be checking/updating pw history here? */ if(pol.pw_history_num > 1) { if(adb.admin_history_kvno != hist_kvno) { ret = KADM5_BAD_HIST_KEY; goto done; } if (ret = check_pw_reuse(handle->context, &hist_key, kdb.n_key_data, kdb.key_data, adb.old_key_len, adb.old_keys)) goto done; } #endif if (pol.pw_max_life) kdb.pw_expiration = now + pol.pw_max_life; else kdb.pw_expiration = 0; } else { kdb.pw_expiration = 0; } ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now); if (ret) goto done; if ((ret = kdb_put_entry(handle, &kdb, &adb))) goto done; ret = KADM5_OK; done: for (i = 0; i < tmp_key_data.key_data_ver; i++) { if (tmp_key_data.key_data_contents[i]) { memset (tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]); free (tmp_key_data.key_data_contents[i]); } } kdb_free_entry(handle, &kdb, &adb); if (have_pol) kadm5_free_policy_ent(handle->lhandle, &pol); return ret; } #endif kadm5_ret_t kadm5_setkey_principal(void *server_handle, krb5_principal principal, krb5_keyblock *keyblocks, int n_keys) { return kadm5_setkey_principal_3(server_handle, principal, FALSE, 0, NULL, keyblocks, n_keys); } kadm5_ret_t kadm5_setkey_principal_3(void *server_handle, krb5_principal principal, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, krb5_keyblock *keyblocks, int n_keys) { krb5_db_entry kdb; osa_princ_ent_rec adb; krb5_int32 now; kadm5_policy_ent_rec pol; krb5_key_data *old_key_data; int n_old_keys; int i, j, k, kvno, ret, have_pol = 0; #if 0 int last_pwd; #endif kadm5_server_handle_t handle = server_handle; krb5_boolean similar; krb5_keysalt keysalt; krb5_key_data tmp_key_data; krb5_key_data *tptr; CHECK_HANDLE(server_handle); krb5_clear_error_message(handle->context); if (principal == NULL || keyblocks == NULL) return EINVAL; if (hist_princ && /* this will be NULL when initializing the databse */ ((krb5_principal_compare(handle->context, principal, hist_princ)) == TRUE)) return KADM5_PROTECT_PRINCIPAL; for (i = 0; i < n_keys; i++) { for (j = i+1; j < n_keys; j++) { if ((ret = krb5_c_enctype_compare(handle->context, keyblocks[i].enctype, keyblocks[j].enctype, &similar))) return(ret); if (similar) { if (n_ks_tuple) { if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype) return KADM5_SETKEY_DUP_ENCTYPES; } else return KADM5_SETKEY_DUP_ENCTYPES; } } } if (n_ks_tuple && n_ks_tuple != n_keys) return KADM5_SETKEY3_ETYPE_MISMATCH; if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) return(ret); for (kvno = 0, i=0; i kvno) kvno = kdb.key_data[i].key_data_kvno; if (keepold) { old_key_data = kdb.key_data; n_old_keys = kdb.n_key_data; } else { if (kdb.key_data != NULL) cleanup_key_data(handle->context, kdb.n_key_data, kdb.key_data); n_old_keys = 0; old_key_data = NULL; } kdb.key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, (n_keys+n_old_keys) *sizeof(krb5_key_data)); if (kdb.key_data == NULL) { ret = ENOMEM; goto done; } memset(kdb.key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data)); kdb.n_key_data = 0; for (i = 0; i < n_keys; i++) { if (n_ks_tuple) { keysalt.type = ks_tuple[i].ks_salttype; keysalt.data.length = 0; keysalt.data.data = NULL; if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) { ret = KADM5_SETKEY3_ETYPE_MISMATCH; goto done; } } memset (&tmp_key_data, 0, sizeof(tmp_key_data)); ret = krb5_dbekd_encrypt_key_data(handle->context, &handle->master_keyblock, &keyblocks[i], n_ks_tuple ? &keysalt : NULL, kvno + 1, &tmp_key_data); if (ret) { goto done; } tptr = &kdb.key_data[i]; for (k = 0; k < tmp_key_data.key_data_ver; k++) { tptr->key_data_type[k] = tmp_key_data.key_data_type[k]; tptr->key_data_length[k] = tmp_key_data.key_data_length[k]; if (tmp_key_data.key_data_contents[k]) { tptr->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]); if (tptr->key_data_contents[k] == NULL) { int i1; for (i1 = k; i1 < tmp_key_data.key_data_ver; i1++) { if (tmp_key_data.key_data_contents[i1]) { memset (tmp_key_data.key_data_contents[i1], 0, tmp_key_data.key_data_length[i1]); free (tmp_key_data.key_data_contents[i1]); } } ret = ENOMEM; goto done; } memcpy (tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]); memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]); free (tmp_key_data.key_data_contents[k]); tmp_key_data.key_data_contents[k] = NULL; } } kdb.n_key_data++; } /* copy old key data if necessary */ for (i = 0; i < n_old_keys; i++) { kdb.key_data[i+n_keys] = old_key_data[i]; memset(&old_key_data[i], 0, sizeof (krb5_key_data)); kdb.n_key_data++; } if (old_key_data) krb5_db_free(handle->context, old_key_data); /* assert(kdb.n_key_data == n_keys + n_old_keys) */ kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; if ((ret = krb5_timeofday(handle->context, &now))) goto done; if ((adb.aux_attributes & KADM5_POLICY)) { if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)) != KADM5_OK) goto done; have_pol = 1; #if 0 /* * The spec says this check is overridden if the caller has * modify privilege. The admin server therefore makes this * check itself (in chpass_principal_wrapper, misc.c). A * local caller implicitly has all authorization bits. */ if (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb, &last_pwd)) goto done; if((now - last_pwd) < pol.pw_min_life && !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { ret = KADM5_PASS_TOOSOON; goto done; } #endif #if 0 /* * Should we be checking/updating pw history here? */ if (pol.pw_history_num > 1) { if(adb.admin_history_kvno != hist_kvno) { ret = KADM5_BAD_HIST_KEY; goto done; } if (ret = check_pw_reuse(handle->context, &handle->master_keyblock, &hist_key, kdb.n_key_data, kdb.key_data, adb.old_key_len, adb.old_keys)) goto done; } #endif if (pol.pw_max_life) kdb.pw_expiration = now + pol.pw_max_life; else kdb.pw_expiration = 0; } else { kdb.pw_expiration = 0; } if ((ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))) goto done; if ((ret = kdb_put_entry(handle, &kdb, &adb))) goto done; ret = KADM5_OK; done: kdb_free_entry(handle, &kdb, &adb); if (have_pol) kadm5_free_policy_ent(handle->lhandle, &pol); return ret; } /* * Allocate an array of n_key_data krb5_keyblocks, fill in each * element with the results of decrypting the nth key in key_data with * master_keyblock, and if n_keys is not NULL fill it in with the * number of keys decrypted. */ static int decrypt_key_data(krb5_context context, krb5_keyblock *master_keyblock, int n_key_data, krb5_key_data *key_data, krb5_keyblock **keyblocks, int *n_keys) { krb5_keyblock *keys; int ret, i; keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock)); if (keys == NULL) return ENOMEM; memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock)); for (i = 0; i < n_key_data; i++) { ret = krb5_dbekd_decrypt_key_data(context, master_keyblock, &key_data[i], &keys[i], NULL); if (ret) { for (; i >= 0; i--) { if (keys[i].contents) { memset (keys[i].contents, 0, keys[i].length); free( keys[i].contents ); } } memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock)); free(keys); return ret; } } *keyblocks = keys; if (n_keys) *n_keys = n_key_data; return 0; } /* * Function: kadm5_decrypt_key * * Purpose: Retrieves and decrypts a principal key. * * Arguments: * * server_handle (r) kadm5 handle * entry (r) principal retrieved with kadm5_get_principal * ktype (r) enctype to search for, or -1 to ignore * stype (r) salt type to search for, or -1 to ignore * kvno (r) kvno to search for, -1 for max, 0 for max * only if it also matches ktype and stype * keyblock (w) keyblock to fill in * keysalt (w) keysalt to fill in, or NULL * kvnop (w) kvno to fill in, or NULL * * Effects: Searches the key_data array of entry, which must have been * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to * find a key with a specified enctype, salt type, and kvno in a * principal entry. If not found, return ENOENT. Otherwise, decrypt * it with the master key, and return the key in keyblock, the salt * in salttype, and the key version number in kvno. * * If ktype or stype is -1, it is ignored for the search. If kvno is * -1, ktype and stype are ignored and the key with the max kvno is * returned. If kvno is 0, only the key with the max kvno is returned * and only if it matches the ktype and stype; otherwise, ENOENT is * returned. */ kadm5_ret_t kadm5_decrypt_key(void *server_handle, kadm5_principal_ent_t entry, krb5_int32 ktype, krb5_int32 stype, krb5_int32 kvno, krb5_keyblock *keyblock, krb5_keysalt *keysalt, int *kvnop) { kadm5_server_handle_t handle = server_handle; krb5_db_entry dbent; krb5_key_data *key_data; int ret; CHECK_HANDLE(server_handle); if (entry->n_key_data == 0 || entry->key_data == NULL) return EINVAL; /* find_enctype only uses these two fields */ dbent.n_key_data = entry->n_key_data; dbent.key_data = entry->key_data; if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype, stype, kvno, &key_data))) return ret; if ((ret = krb5_dbekd_decrypt_key_data(handle->context, &handle->master_keyblock, key_data, keyblock, keysalt))) return ret; /* * Coerce the enctype of the output keyblock in case we got an * inexact match on the enctype; this behavior will go away when * the key storage architecture gets redesigned for 1.3. */ keyblock->enctype = ktype; if (kvnop) *kvnop = key_data->key_data_kvno; return KADM5_OK; } /* Solaris Kerberos */ kadm5_ret_t kadm5_check_min_life(void *server_handle, krb5_principal principal, char *msg_ret, unsigned int msg_len) { krb5_int32 now; kadm5_ret_t ret; kadm5_policy_ent_rec pol; kadm5_principal_ent_rec princ; kadm5_server_handle_t handle = server_handle; if (msg_ret != NULL) *msg_ret = '\0'; ret = krb5_timeofday(handle->context, &now); if (ret) return ret; ret = kadm5_get_principal(handle->lhandle, principal, &princ, KADM5_PRINCIPAL_NORMAL_MASK); if(ret) return ret; if(princ.aux_attributes & KADM5_POLICY) { if((ret=kadm5_get_policy(handle->lhandle, princ.policy, &pol)) != KADM5_OK) { (void) kadm5_free_principal_ent(handle->lhandle, &princ); return ret; } if((now - princ.last_pwd_change) < pol.pw_min_life && !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { if (msg_ret != NULL) { time_t until; char *time_string, *ptr, *errstr; until = princ.last_pwd_change + pol.pw_min_life; time_string = ctime(&until); errstr = (char *)error_message(CHPASS_UTIL_PASSWORD_TOO_SOON); if (strlen(errstr) + strlen(time_string) >= msg_len) { *errstr = '\0'; } else { if (*(ptr = &time_string[strlen(time_string)-1]) == '\n') *ptr = '\0'; sprintf(msg_ret, errstr, time_string); } } (void) kadm5_free_policy_ent(handle->lhandle, &pol); (void) kadm5_free_principal_ent(handle->lhandle, &princ); return KADM5_PASS_TOOSOON; } ret = kadm5_free_policy_ent(handle->lhandle, &pol); if (ret) { (void) kadm5_free_principal_ent(handle->lhandle, &princ); return ret; } } return kadm5_free_principal_ent(handle->lhandle, &princ); } /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* @(#)xdr_mem.c 2.1 88/07/29 4.0 RPCSRC */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ #if !defined(lint) && defined(SCCSIDS) static char sccsid[] = "@(#)xdr_mem.c 1.19 87/08/11 Copyr 1984 Sun Micro"; #endif /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. * */ #include "admin.h" #include #include #include /* Solaris Kerberos - 116 resync */ static bool_t xdralloc_putlong(); static bool_t xdralloc_putbytes(); static unsigned int xdralloc_getpos(); static rpc_inline_t * xdralloc_inline(); static void xdralloc_destroy(); static bool_t xdralloc_notsup_getlong(); static bool_t xdralloc_notsup_getbytes(); static bool_t xdralloc_notsup_setpos(); static struct xdr_ops xdralloc_ops = { xdralloc_notsup_getlong, xdralloc_putlong, xdralloc_notsup_getbytes, xdralloc_putbytes, xdralloc_getpos, xdralloc_notsup_setpos, xdralloc_inline, xdralloc_destroy, }; /* * The procedure xdralloc_create initializes a stream descriptor for a * memory buffer. */ void xdralloc_create(XDR *xdrs, enum xdr_op op) { xdrs->x_op = op; xdrs->x_ops = &xdralloc_ops; xdrs->x_private = (caddr_t) DynCreate(sizeof(char), -4); /* not allowed to fail */ } caddr_t xdralloc_getdata(XDR *xdrs) { return (caddr_t) DynGet((DynObject) xdrs->x_private, 0); } void xdralloc_release(XDR *xdrs) { DynRelease((DynObject) xdrs->x_private); } static void xdralloc_destroy(XDR *xdrs) { DynDestroy((DynObject) xdrs->x_private); } static bool_t xdralloc_notsup_getlong( register XDR *xdrs, long *lp) { return FALSE; } static bool_t xdralloc_putlong( register XDR *xdrs, long *lp) { int l = htonl((uint32_t) *lp); /* XXX need bounds checking */ /* XXX assumes sizeof(int)==4 */ if (DynInsert((DynObject) xdrs->x_private, DynSize((DynObject) xdrs->x_private), &l, sizeof(int)) != DYN_OK) return FALSE; return (TRUE); } static bool_t xdralloc_notsup_getbytes( register XDR *xdrs, caddr_t addr, register unsigned int len) { return FALSE; } static bool_t xdralloc_putbytes( register XDR *xdrs, caddr_t addr, register unsigned int len) { if (DynInsert((DynObject) xdrs->x_private, DynSize((DynObject) xdrs->x_private), addr, (int) len) != DYN_OK) return FALSE; return TRUE; } static unsigned int xdralloc_getpos(XDR *xdrs) { return DynSize((DynObject) xdrs->x_private); } static bool_t xdralloc_notsup_setpos( register XDR *xdrs, unsigned int lp) { return FALSE; } static rpc_inline_t *xdralloc_inline( register XDR *xdrs, int len) { return (rpc_inline_t *) 0; } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * lib/kadm/str_conv.c * * Copyright 1995 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ /* * str_conv.c - Convert between strings and Kerberos internal data. */ /* * Table of contents: * * String decoding: * ---------------- * krb5_string_to_flags() - Convert string to krb5_flags. * * String encoding: * ---------------- * krb5_flags_to_string() - Convert krb5_flags to string. */ #include "k5-int.h" #include "admin_internal.h" #include "adm_proto.h" /* * Local data structures. */ struct flags_lookup_entry { krb5_flags fl_flags; /* Flag */ krb5_boolean fl_sense; /* Sense of the flag */ const char * fl_specifier; /* How to recognize it */ const char * fl_output; /* How to spit it out */ }; /* * Local strings */ static const char default_tupleseps[] = ", \t"; static const char default_ksaltseps[] = ":."; /* Keytype strings */ /* Flags strings */ static const char flags_pdate_in[] = "postdateable"; static const char flags_fwd_in[] = "forwardable"; static const char flags_tgtbased_in[] = "tgt-based"; static const char flags_renew_in[] = "renewable"; static const char flags_proxy_in[] = "proxiable"; static const char flags_dup_skey_in[] = "dup-skey"; static const char flags_tickets_in[] = "allow-tickets"; static const char flags_preauth_in[] = "preauth"; static const char flags_hwauth_in[] = "hwauth"; static const char flags_pwchange_in[] = "pwchange"; static const char flags_service_in[] = "service"; static const char flags_pwsvc_in[] = "pwservice"; static const char flags_md5_in[] = "md5"; static const char flags_pdate_out[] = "Not Postdateable"; static const char flags_fwd_out[] = "Not Forwardable"; static const char flags_tgtbased_out[] = "No TGT-based requests"; static const char flags_renew_out[] = "Not renewable"; static const char flags_proxy_out[] = "Not proxiable"; static const char flags_dup_skey_out[] = "No DUP_SKEY requests"; static const char flags_tickets_out[] = "All Tickets Disallowed"; static const char flags_preauth_out[] = "Preauthorization required"; static const char flags_hwauth_out[] = "HW Authorization required"; static const char flags_pwchange_out[] = "Password Change required"; static const char flags_service_out[] = "Service Disabled"; static const char flags_pwsvc_out[] = "Password Changing Service"; static const char flags_md5_out[] = "RSA-MD5 supported"; static const char flags_default_neg[] = "-"; static const char flags_default_sep[] = " "; /* * Lookup tables. */ static const struct flags_lookup_entry flags_table[] = { /* flag sense input specifier output string */ /*----------------------------- ------- ------------------ ------------------*/ { KRB5_KDB_DISALLOW_POSTDATED, 0, flags_pdate_in, flags_pdate_out }, { KRB5_KDB_DISALLOW_FORWARDABLE,0, flags_fwd_in, flags_fwd_out }, { KRB5_KDB_DISALLOW_TGT_BASED, 0, flags_tgtbased_in, flags_tgtbased_out}, { KRB5_KDB_DISALLOW_RENEWABLE, 0, flags_renew_in, flags_renew_out }, { KRB5_KDB_DISALLOW_PROXIABLE, 0, flags_proxy_in, flags_proxy_out }, { KRB5_KDB_DISALLOW_DUP_SKEY, 0, flags_dup_skey_in, flags_dup_skey_out}, { KRB5_KDB_DISALLOW_ALL_TIX, 0, flags_tickets_in, flags_tickets_out }, { KRB5_KDB_REQUIRES_PRE_AUTH, 1, flags_preauth_in, flags_preauth_out }, { KRB5_KDB_REQUIRES_HW_AUTH, 1, flags_hwauth_in, flags_hwauth_out }, { KRB5_KDB_REQUIRES_PWCHANGE, 1, flags_pwchange_in, flags_pwchange_out}, { KRB5_KDB_DISALLOW_SVR, 0, flags_service_in, flags_service_out }, { KRB5_KDB_PWCHANGE_SERVICE, 1, flags_pwsvc_in, flags_pwsvc_out }, { KRB5_KDB_SUPPORT_DESMD5, 1, flags_md5_in, flags_md5_out } }; static const int flags_table_nents = sizeof(flags_table)/ sizeof(flags_table[0]); krb5_error_code krb5_string_to_flags(string, positive, negative, flagsp) char * string; const char * positive; const char * negative; krb5_flags * flagsp; { int i; int found; const char *neg; size_t nsize, psize; int cpos; int sense; found = 0; /* We need to have a way to negate it. */ neg = (negative) ? negative : flags_default_neg; nsize = strlen(neg); psize = (positive) ? strlen(positive) : 0; cpos = 0; sense = 1; /* First check for positive or negative sense */ if (!strncasecmp(neg, string, nsize)) { sense = 0; cpos += (int) nsize; } else if (psize && !strncasecmp(positive, string, psize)) { cpos += (int) psize; } for (i=0; i= flags_table_nents) return ENOENT; /* End of list */ if(strlen(flags_table[flag].fl_specifier) > buflen) return ENOMEM; strcpy(buffer, flags_table[flag].fl_specifier); return 0; } /* * krb5_keysalt_is_present() - Determine if a key/salt pair is present * in a list of key/salt tuples. * * Salttype may be negative to indicate a search for only a enctype. */ krb5_boolean krb5_keysalt_is_present(ksaltlist, nksalts, enctype, salttype) krb5_key_salt_tuple *ksaltlist; krb5_int32 nksalts; krb5_enctype enctype; krb5_int32 salttype; { krb5_boolean foundit; int i; foundit = 0; if (ksaltlist) { for (i=0; i * or * */ sp = (char *) NULL; /* Attempt to find a separator */ septmp = ksseplist; for (sp = strchr(kp, (int) *septmp); *(++septmp) && !sp; sp = strchr(kp, (int)*septmp)); /* Solaris Kerberos */ if (sp) { /* Separate enctype from salttype */ sepchar = *sp; *sp = '\0'; sp++; } else stype = -1; /* * Attempt to parse enctype and salttype. If we parse well * then make sure that it specifies a unique key/salt combo */ if (!(kret = krb5_string_to_enctype(kp, &ktype)) && (!sp || !(kret = krb5_string_to_salttype(sp, &stype))) && (dups || !krb5_keysalt_is_present(*ksaltp, *nksaltp, ktype, stype))) { /* Squirrel away old keysalt array */ savep = *ksaltp; len = (size_t) *nksaltp; /* Get new keysalt array */ *ksaltp = (krb5_key_salt_tuple *) malloc((len + 1) * sizeof(krb5_key_salt_tuple)); if (*ksaltp) { /* Copy old keysalt if appropriate */ if (savep) { memcpy(*ksaltp, savep, len * sizeof(krb5_key_salt_tuple)); krb5_xfree(savep); } /* Save our values */ (*ksaltp)[(*nksaltp)].ks_enctype = ktype; (*ksaltp)[(*nksaltp)].ks_salttype = stype; (*nksaltp)++; } else { *ksaltp = savep; break; } } /* * Solaris Kerberos * If the string did not yield a valid enctype/keysalt * just ignore it and continue on. MIT kerberos stops * searching when if finds an unknown string. */ if (sp) sp[-1] = sepchar; if (ep) ep[-1] = trailchar; kp = ep; /* Skip over extra separators - like spaces */ if (kp && *tseplist) { septmp = tseplist; while(*septmp && *kp) { if(*septmp == *kp) { /* Increment string - reset separator list */ kp++; septmp = tseplist; } else { septmp++; } } if (!*kp) kp = NULL; } } /* while kp */ return(kret); } /* * krb5_keysalt_iterate() - Do something for each unique key/salt * combination. * * If ignoresalt set, then salttype is ignored. */ krb5_error_code krb5_keysalt_iterate(ksaltlist, nksalt, ignoresalt, iterator, arg) krb5_key_salt_tuple *ksaltlist; krb5_int32 nksalt; krb5_boolean ignoresalt; krb5_error_code (*iterator) (krb5_key_salt_tuple *, krb5_pointer); krb5_pointer arg; { int i; krb5_error_code kret; krb5_key_salt_tuple scratch; kret = 0; for (i=0; i $@ # libkdb5 needs to link against some files from kadm5 KADM5DIR= $(SRC)/lib/krb5/kadm5 KADM5OBJS= alt_prof.o str_conv.o KADM5SRCS= $(KADM5DIR)/$(KADM5OBJS:%.o=%.c) OBJECTS= $(KDBOBJS) $(KADM5OBJS) $(DERIVED_OBJS) # include library definitions include ../../Makefile.lib SRCS= $(KDBOBJS:%.o=../%.c) SRCS+= $(DERIVED_OBJS:%.o=../%.c) SRCS+= $(KADM5SRCS) LIBS= $(DYNLIB) include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 POFILE = $(LIBRARY:%.a=%.po) POFILES = generic.po # override liblink INS.liblink= -$(RM) $@; $(SYMLINK) $(LIBLINKS)$(VERS) $@ CPPFLAGS += -DHAVE_CONFIG_H -DHAVE_BT_RSEQ \ -I$(KRB5IPROPDIR) \ -I$(SRC)/lib/krb5 \ -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/lib/gss_mechs/mech_krb5/krb5/os \ -I$(SRC)/lib/gss_mechs/mech_krb5/include/krb5 \ -I$(SRC)/uts/common/gssapi/include/ \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include # Hammerhead: -I.. must be in CPPFLAGS (not CFLAGS) to survive Makefile.master.64 reset CFLAGS += $(CCVERBOSE) CPPFLAGS += -I.. CERRWARN += -Wno-unused-variable CERRWARN += -Wno-unused-function CERRWARN += -Wno-type-limits CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-parentheses CERRWARN += -Wno-pointer-to-int-cast # Hammerhead: rpcgen-generated iprop_xdr.c has int64_t* vs longlong_t* mismatch CERRWARN += -Wno-incompatible-pointer-types SMOFF += indenting,all_func_returns,deref_check,signed DYNFLAGS += $(KRUNPATH) $(KMECHLIB) LDLIBS += -lc -lnsl .KEEP_STATE: all: $(LIBS) # Hammerhead: pre-generated (rpcgen + GNU cpp truncation bug) # iprop.h and iprop_xdr.c are now committed source files. # CLEANFILES += $(ISRCHDR) $(ISRCXDR) # Hammerhead: GNU Make parallel builds need PICS to depend on generated files. # $(LIBS): $(ISRCHDR) only ensures iprop.h before link, not before .o compilation. $(PICS): $(ISRCHDR) $(ISRCXDR) # include library targets include ../../Makefile.targ FRC: generic.po: FRC $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext ../*.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ #include #define OSA_ADB_NOERR (28810240L) #define OSA_ADB_DUP (28810241L) #define OSA_ADB_NOENT (28810242L) #define OSA_ADB_DBINIT (28810243L) #define OSA_ADB_BAD_POLICY (28810244L) #define OSA_ADB_BAD_PRINC (28810245L) #define OSA_ADB_BAD_DB (28810246L) #define OSA_ADB_XDR_FAILURE (28810247L) #define OSA_ADB_FAILURE (28810248L) #define OSA_ADB_BADLOCKMODE (28810249L) #define OSA_ADB_CANTLOCK_DB (28810250L) #define OSA_ADB_NOTLOCKED (28810251L) #define OSA_ADB_NOLOCKFILE (28810252L) #define OSA_ADB_NOEXCL_PERM (28810253L) #define ERROR_TABLE_BASE_adb (28810240L) extern const struct error_table et_adb_error_table; #if !defined(_WIN32) /* for compatibility with older versions... */ extern void initialize_adb_error_table (void) /*@modifies internalState@*/; #else #define initialize_adb_error_table() #endif #if !defined(_WIN32) #define init_adb_err_tbl initialize_adb_error_table #define adb_err_base ERROR_TABLE_BASE_adb #endif # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2025 Hammerhead Project # include ../Makefile.com include $(SRC)/lib/Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * lib/kdb/decrypt_key.c * * Copyright 1990,1991 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * * krb5_kdb_encrypt_key(), krb5_kdb_decrypt_key functions */ /* * Copyright (C) 1998 by the FundsXpress, INC. * * All rights reserved. * * Export of this software from the United States of America may require * a specific license from the United States Government. It is the * responsibility of any person or organization contemplating export to * obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. FundsXpress makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "k5-int.h" #include /* * Decrypt a key from storage in the database. "eblock" is used * to decrypt the key in "in" into "out"; the storage pointed to by "out" * is allocated before use. */ krb5_error_code krb5_dbekd_decrypt_key_data( krb5_context context, const krb5_keyblock * mkey, const krb5_key_data * key_data, krb5_keyblock * dbkey, krb5_keysalt * keysalt) { krb5_error_code retval = 0; krb5_int16 tmplen; krb5_octet * ptr; krb5_enc_data cipher; krb5_data plain; ptr = key_data->key_data_contents[0]; if (ptr) { krb5_kdb_decode_int16(ptr, tmplen); ptr += 2; cipher.enctype = ENCTYPE_UNKNOWN; cipher.ciphertext.length = key_data->key_data_length[0]-2; cipher.ciphertext.data = (char *)ptr; /* SUNWresync121 XXX */ plain.length = key_data->key_data_length[0]-2; if ((plain.data = (char *) malloc(plain.length)) == NULL) return(ENOMEM); (void) memset(plain.data, 0, plain.length); if ((retval = krb5_c_decrypt(context, mkey, 0 /* XXX */, 0, &cipher, &plain))) { krb5_xfree(plain.data); return retval; } /* tmplen is the true length of the key. plain.data is the plaintext data length, but it may be padded, since the old-style etypes didn't store the real length. I can check to make sure that there are enough bytes, but I can't do any better than that. */ if (tmplen > plain.length) { krb5_xfree(plain.data); return(KRB5_CRYPTO_INTERNAL); } dbkey->magic = KV5M_KEYBLOCK; dbkey->enctype = key_data->key_data_type[0]; dbkey->length = tmplen; dbkey->contents = (unsigned char *) plain.data; /* SUNWresync121 XXX */ dbkey->dk_list = NULL; dbkey->hKey = CK_INVALID_HANDLE; } /* Decode salt data */ if (keysalt) { if (key_data->key_data_ver == 2) { keysalt->type = key_data->key_data_type[1]; if ((keysalt->data.length = key_data->key_data_length[1])) { if (!(keysalt->data.data=(char *)malloc(keysalt->data.length))){ if (key_data->key_data_contents[0]) { krb5_xfree(dbkey->contents); dbkey->contents = 0; dbkey->length = 0; } return ENOMEM; } memcpy(keysalt->data.data, key_data->key_data_contents[1], (size_t) keysalt->data.length); } else keysalt->data.data = (char *) NULL; } else { keysalt->type = KRB5_KDB_SALTTYPE_NORMAL; keysalt->data.data = (char *) NULL; keysalt->data.length = 0; } } return retval; } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * lib/kdb/encrypt_key.c * * Copyright 1990,1991 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * * krb5_kdb_encrypt_key(), krb5_kdb_decrypt_key functions */ /* * Copyright (C) 1998 by the FundsXpress, INC. * * All rights reserved. * * Export of this software from the United States of America may require * a specific license from the United States Government. It is the * responsibility of any person or organization contemplating export to * obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. FundsXpress makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "k5-int.h" #include /* * Encrypt a key for storage in the database. "eblock" is used * to encrypt the key in "in" into "out"; the storage pointed to by "out" * is allocated before use. */ krb5_error_code krb5_dbekd_encrypt_key_data( krb5_context context, const krb5_keyblock * mkey, const krb5_keyblock * dbkey, const krb5_keysalt * keysalt, int keyver, krb5_key_data * key_data) { krb5_error_code retval; krb5_octet * ptr; size_t len; int i; krb5_data plain; krb5_enc_data cipher; for (i = 0; i < key_data->key_data_ver; i++) if (key_data->key_data_contents[i]) krb5_xfree(key_data->key_data_contents[i]); key_data->key_data_ver = 1; key_data->key_data_kvno = keyver; /* * The First element of the type/length/contents * fields is the key type/length/contents */ if ((retval = krb5_c_encrypt_length(context, mkey->enctype, dbkey->length, &len))) return(retval); if ((ptr = (krb5_octet *) malloc(2 + len)) == NULL) return(ENOMEM); (void) memset(ptr, 0, 2 + len); key_data->key_data_type[0] = dbkey->enctype; key_data->key_data_length[0] = 2 + len; key_data->key_data_contents[0] = ptr; krb5_kdb_encode_int16(dbkey->length, ptr); ptr += 2; plain.length = dbkey->length; plain.data = (char *)dbkey->contents; /* SUNWresync121 XXX */ cipher.ciphertext.length = len; cipher.ciphertext.data = (char *)ptr; /* SUNWresync121 XXX */ if ((retval = krb5_c_encrypt(context, mkey, /* XXX */ 0, 0, &plain, &cipher))) { krb5_xfree(key_data->key_data_contents[0]); return retval; } /* After key comes the salt in necessary */ if (keysalt) { if (keysalt->type > 0) { key_data->key_data_ver++; key_data->key_data_type[1] = keysalt->type; if ((key_data->key_data_length[1] = keysalt->data.length) != 0) { key_data->key_data_contents[1] = (krb5_octet *)malloc(keysalt->data.length); if (key_data->key_data_contents[1] == NULL) { krb5_xfree(key_data->key_data_contents[0]); return ENOMEM; } memcpy(key_data->key_data_contents[1], keysalt->data.data, (size_t) keysalt->data.length); } } } return retval; } /* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _IPROP_H_RPCGEN #define _IPROP_H_RPCGEN #include typedef struct { u_int utf8str_t_len; char *utf8str_t_val; } utf8str_t; typedef uint32_t kdb_sno_t; struct kdbe_time_t { uint32_t seconds; uint32_t useconds; }; typedef struct kdbe_time_t kdbe_time_t; struct kdbe_key_t { int32_t k_ver; int32_t k_kvno; struct { u_int k_enctype_len; int32_t *k_enctype_val; } k_enctype; struct { u_int k_contents_len; utf8str_t *k_contents_val; } k_contents; }; typedef struct kdbe_key_t kdbe_key_t; struct kdbe_data_t { int32_t k_magic; utf8str_t k_data; }; typedef struct kdbe_data_t kdbe_data_t; struct kdbe_princ_t { utf8str_t k_realm; struct { u_int k_components_len; kdbe_data_t *k_components_val; } k_components; int32_t k_nametype; }; typedef struct kdbe_princ_t kdbe_princ_t; struct kdbe_tl_t { int16_t tl_type; struct { u_int tl_data_len; char *tl_data_val; } tl_data; }; typedef struct kdbe_tl_t kdbe_tl_t; typedef struct { u_int kdbe_pw_hist_t_len; kdbe_key_t *kdbe_pw_hist_t_val; } kdbe_pw_hist_t; enum kdbe_attr_type_t { AT_ATTRFLAGS = 0, AT_MAX_LIFE = 1, AT_MAX_RENEW_LIFE = 2, AT_EXP = 3, AT_PW_EXP = 4, AT_LAST_SUCCESS = 5, AT_LAST_FAILED = 6, AT_FAIL_AUTH_COUNT = 7, AT_PRINC = 8, AT_KEYDATA = 9, AT_TL_DATA = 10, AT_LEN = 11, AT_MOD_PRINC = 12, AT_MOD_TIME = 13, AT_MOD_WHERE = 14, AT_PW_LAST_CHANGE = 15, AT_PW_POLICY = 16, AT_PW_POLICY_SWITCH = 17, AT_PW_HIST_KVNO = 18, AT_PW_HIST = 19 }; typedef enum kdbe_attr_type_t kdbe_attr_type_t; struct kdbe_val_t { kdbe_attr_type_t av_type; union { uint32_t av_attrflags; uint32_t av_max_life; uint32_t av_max_renew_life; uint32_t av_exp; uint32_t av_pw_exp; uint32_t av_last_success; uint32_t av_last_failed; uint32_t av_fail_auth_count; kdbe_princ_t av_princ; struct { u_int av_keydata_len; kdbe_key_t *av_keydata_val; } av_keydata; struct { u_int av_tldata_len; kdbe_tl_t *av_tldata_val; } av_tldata; int16_t av_len; uint32_t av_pw_last_change; kdbe_princ_t av_mod_princ; uint32_t av_mod_time; utf8str_t av_mod_where; utf8str_t av_pw_policy; bool_t av_pw_policy_switch; uint32_t av_pw_hist_kvno; struct { u_int av_pw_hist_len; kdbe_pw_hist_t *av_pw_hist_val; } av_pw_hist; struct { u_int av_extension_len; char *av_extension_val; } av_extension; } kdbe_val_t_u; }; typedef struct kdbe_val_t kdbe_val_t; typedef struct { u_int kdbe_t_len; kdbe_val_t *kdbe_t_val; } kdbe_t; struct kdb_incr_update_t { utf8str_t kdb_princ_name; kdb_sno_t kdb_entry_sno; kdbe_time_t kdb_time; kdbe_t kdb_update; bool_t kdb_deleted; bool_t kdb_commit; struct { u_int kdb_kdcs_seen_by_len; utf8str_t *kdb_kdcs_seen_by_val; } kdb_kdcs_seen_by; struct { u_int kdb_futures_len; char *kdb_futures_val; } kdb_futures; }; typedef struct kdb_incr_update_t kdb_incr_update_t; typedef struct { u_int kdb_ulog_t_len; kdb_incr_update_t *kdb_ulog_t_val; } kdb_ulog_t; enum update_status_t { UPDATE_OK = 0, UPDATE_ERROR = 1, UPDATE_FULL_RESYNC_NEEDED = 2, UPDATE_BUSY = 3, UPDATE_NIL = 4, UPDATE_PERM_DENIED = 5 }; typedef enum update_status_t update_status_t; struct kdb_last_t { kdb_sno_t last_sno; kdbe_time_t last_time; }; typedef struct kdb_last_t kdb_last_t; struct kdb_incr_result_t { kdb_last_t lastentry; kdb_ulog_t updates; update_status_t ret; }; typedef struct kdb_incr_result_t kdb_incr_result_t; struct kdb_fullresync_result_t { kdb_last_t lastentry; update_status_t ret; }; typedef struct kdb_fullresync_result_t kdb_fullresync_result_t; #define KRB5_IPROP_PROG 100423 #define KRB5_IPROP_VERS 1 #define IPROP_NULL 0 extern void * iprop_null_1(); #define IPROP_GET_UPDATES 1 extern kdb_incr_result_t * iprop_get_updates_1(); #define IPROP_FULL_RESYNC 2 extern kdb_fullresync_result_t * iprop_full_resync_1(); extern int krb5_iprop_prog_1_freeresult(); /* the xdr functions */ extern bool_t xdr_utf8str_t(); extern bool_t xdr_kdb_sno_t(); extern bool_t xdr_kdbe_time_t(); extern bool_t xdr_kdbe_key_t(); extern bool_t xdr_kdbe_data_t(); extern bool_t xdr_kdbe_princ_t(); extern bool_t xdr_kdbe_tl_t(); extern bool_t xdr_kdbe_pw_hist_t(); extern bool_t xdr_kdbe_attr_type_t(); extern bool_t xdr_kdbe_val_t(); extern bool_t xdr_kdbe_t(); extern bool_t xdr_kdb_incr_update_t(); extern bool_t xdr_kdb_ulog_t(); extern bool_t xdr_update_status_t(); extern bool_t xdr_kdb_last_t(); extern bool_t xdr_kdb_incr_result_t(); extern bool_t xdr_kdb_fullresync_result_t(); #endif /* !_IPROP_H_RPCGEN */ /* * Please do not edit this file. * It was generated using rpcgen. */ #include "iprop.h" #ifndef _KERNEL #include #endif /* !_KERNEL */ #include "iprop.h" bool_t xdr_int16_t(xdrs, objp) XDR *xdrs; int16_t *objp; { rpc_inline_t *buf __unused; if (!xdr_short(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_uint16_t(xdrs, objp) XDR *xdrs; uint16_t *objp; { rpc_inline_t *buf __unused; if (!xdr_u_short(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_int32_t(xdrs, objp) XDR *xdrs; int32_t *objp; { rpc_inline_t *buf __unused; if (!xdr_int(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_uint32_t(xdrs, objp) XDR *xdrs; uint32_t *objp; { rpc_inline_t *buf __unused; if (!xdr_u_int(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_int64_t(xdrs, objp) XDR *xdrs; int64_t *objp; { rpc_inline_t *buf __unused; if (!xdr_longlong_t(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_uint64_t(xdrs, objp) XDR *xdrs; uint64_t *objp; { rpc_inline_t *buf __unused; if (!xdr_u_longlong_t(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_utf8str_t(xdrs, objp) XDR *xdrs; utf8str_t *objp; { rpc_inline_t *buf __unused; if (!xdr_bytes(xdrs, (char **)&objp->utf8str_t_val, (u_int *) &objp->utf8str_t_len, ~0)) return (FALSE); return (TRUE); } bool_t xdr_kdb_sno_t(xdrs, objp) XDR *xdrs; kdb_sno_t *objp; { rpc_inline_t *buf __unused; if (!xdr_uint32_t(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_time_t(xdrs, objp) XDR *xdrs; kdbe_time_t *objp; { rpc_inline_t *buf __unused; if (!xdr_uint32_t(xdrs, &objp->seconds)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->useconds)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_key_t(xdrs, objp) XDR *xdrs; kdbe_key_t *objp; { rpc_inline_t *buf __unused; if (!xdr_int32_t(xdrs, &objp->k_ver)) return (FALSE); if (!xdr_int32_t(xdrs, &objp->k_kvno)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->k_enctype.k_enctype_val, (u_int *) &objp->k_enctype.k_enctype_len, ~0, sizeof (int32_t), (xdrproc_t)xdr_int32_t)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->k_contents.k_contents_val, (u_int *) &objp->k_contents.k_contents_len, ~0, sizeof (utf8str_t), (xdrproc_t)xdr_utf8str_t)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_data_t(xdrs, objp) XDR *xdrs; kdbe_data_t *objp; { rpc_inline_t *buf __unused; if (!xdr_int32_t(xdrs, &objp->k_magic)) return (FALSE); if (!xdr_utf8str_t(xdrs, &objp->k_data)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_princ_t(xdrs, objp) XDR *xdrs; kdbe_princ_t *objp; { rpc_inline_t *buf __unused; if (!xdr_utf8str_t(xdrs, &objp->k_realm)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->k_components.k_components_val, (u_int *) &objp->k_components.k_components_len, ~0, sizeof (kdbe_data_t), (xdrproc_t)xdr_kdbe_data_t)) return (FALSE); if (!xdr_int32_t(xdrs, &objp->k_nametype)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_tl_t(xdrs, objp) XDR *xdrs; kdbe_tl_t *objp; { rpc_inline_t *buf __unused; if (!xdr_int16_t(xdrs, &objp->tl_type)) return (FALSE); if (!xdr_bytes(xdrs, (char **)&objp->tl_data.tl_data_val, (u_int *) &objp->tl_data.tl_data_len, ~0)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_pw_hist_t(xdrs, objp) XDR *xdrs; kdbe_pw_hist_t *objp; { rpc_inline_t *buf __unused; if (!xdr_array(xdrs, (char **)&objp->kdbe_pw_hist_t_val, (u_int *) &objp->kdbe_pw_hist_t_len, ~0, sizeof (kdbe_key_t), (xdrproc_t)xdr_kdbe_key_t)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_attr_type_t(xdrs, objp) XDR *xdrs; kdbe_attr_type_t *objp; { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_val_t(xdrs, objp) XDR *xdrs; kdbe_val_t *objp; { rpc_inline_t *buf __unused; if (!xdr_kdbe_attr_type_t(xdrs, &objp->av_type)) return (FALSE); switch (objp->av_type) { case AT_ATTRFLAGS: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_attrflags)) return (FALSE); break; case AT_MAX_LIFE: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_max_life)) return (FALSE); break; case AT_MAX_RENEW_LIFE: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_max_renew_life)) return (FALSE); break; case AT_EXP: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_exp)) return (FALSE); break; case AT_PW_EXP: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_pw_exp)) return (FALSE); break; case AT_LAST_SUCCESS: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_last_success)) return (FALSE); break; case AT_LAST_FAILED: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_last_failed)) return (FALSE); break; case AT_FAIL_AUTH_COUNT: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_fail_auth_count)) return (FALSE); break; case AT_PRINC: if (!xdr_kdbe_princ_t(xdrs, &objp->kdbe_val_t_u.av_princ)) return (FALSE); break; case AT_KEYDATA: if (!xdr_array(xdrs, (char **)&objp->kdbe_val_t_u.av_keydata.av_keydata_val, (u_int *) &objp->kdbe_val_t_u.av_keydata.av_keydata_len, ~0, sizeof (kdbe_key_t), (xdrproc_t)xdr_kdbe_key_t)) return (FALSE); break; case AT_TL_DATA: if (!xdr_array(xdrs, (char **)&objp->kdbe_val_t_u.av_tldata.av_tldata_val, (u_int *) &objp->kdbe_val_t_u.av_tldata.av_tldata_len, ~0, sizeof (kdbe_tl_t), (xdrproc_t)xdr_kdbe_tl_t)) return (FALSE); break; case AT_LEN: if (!xdr_int16_t(xdrs, &objp->kdbe_val_t_u.av_len)) return (FALSE); break; case AT_PW_LAST_CHANGE: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_pw_last_change)) return (FALSE); break; case AT_MOD_PRINC: if (!xdr_kdbe_princ_t(xdrs, &objp->kdbe_val_t_u.av_mod_princ)) return (FALSE); break; case AT_MOD_TIME: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_mod_time)) return (FALSE); break; case AT_MOD_WHERE: if (!xdr_utf8str_t(xdrs, &objp->kdbe_val_t_u.av_mod_where)) return (FALSE); break; case AT_PW_POLICY: if (!xdr_utf8str_t(xdrs, &objp->kdbe_val_t_u.av_pw_policy)) return (FALSE); break; case AT_PW_POLICY_SWITCH: if (!xdr_bool(xdrs, &objp->kdbe_val_t_u.av_pw_policy_switch)) return (FALSE); break; case AT_PW_HIST_KVNO: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_pw_hist_kvno)) return (FALSE); break; case AT_PW_HIST: if (!xdr_array(xdrs, (char **)&objp->kdbe_val_t_u.av_pw_hist.av_pw_hist_val, (u_int *) &objp->kdbe_val_t_u.av_pw_hist.av_pw_hist_len, ~0, sizeof (kdbe_pw_hist_t), (xdrproc_t)xdr_kdbe_pw_hist_t)) return (FALSE); break; default: if (!xdr_bytes(xdrs, (char **)&objp->kdbe_val_t_u.av_extension.av_extension_val, (u_int *) &objp->kdbe_val_t_u.av_extension.av_extension_len, ~0)) return (FALSE); break; } return (TRUE); } bool_t xdr_kdbe_t(xdrs, objp) XDR *xdrs; kdbe_t *objp; { rpc_inline_t *buf __unused; if (!xdr_array(xdrs, (char **)&objp->kdbe_t_val, (u_int *) &objp->kdbe_t_len, ~0, sizeof (kdbe_val_t), (xdrproc_t)xdr_kdbe_val_t)) return (FALSE); return (TRUE); } bool_t xdr_kdb_incr_update_t(xdrs, objp) XDR *xdrs; kdb_incr_update_t *objp; { rpc_inline_t *buf __unused; if (!xdr_utf8str_t(xdrs, &objp->kdb_princ_name)) return (FALSE); if (!xdr_kdb_sno_t(xdrs, &objp->kdb_entry_sno)) return (FALSE); if (!xdr_kdbe_time_t(xdrs, &objp->kdb_time)) return (FALSE); if (!xdr_kdbe_t(xdrs, &objp->kdb_update)) return (FALSE); if (!xdr_bool(xdrs, &objp->kdb_deleted)) return (FALSE); if (!xdr_bool(xdrs, &objp->kdb_commit)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->kdb_kdcs_seen_by.kdb_kdcs_seen_by_val, (u_int *) &objp->kdb_kdcs_seen_by.kdb_kdcs_seen_by_len, ~0, sizeof (utf8str_t), (xdrproc_t)xdr_utf8str_t)) return (FALSE); if (!xdr_bytes(xdrs, (char **)&objp->kdb_futures.kdb_futures_val, (u_int *) &objp->kdb_futures.kdb_futures_len, ~0)) return (FALSE); return (TRUE); } bool_t xdr_kdb_ulog_t(xdrs, objp) XDR *xdrs; kdb_ulog_t *objp; { rpc_inline_t *buf __unused; if (!xdr_array(xdrs, (char **)&objp->kdb_ulog_t_val, (u_int *) &objp->kdb_ulog_t_len, ~0, sizeof (kdb_incr_update_t), (xdrproc_t)xdr_kdb_incr_update_t)) return (FALSE); return (TRUE); } bool_t xdr_update_status_t(xdrs, objp) XDR *xdrs; update_status_t *objp; { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_kdb_last_t(xdrs, objp) XDR *xdrs; kdb_last_t *objp; { rpc_inline_t *buf __unused; if (!xdr_kdb_sno_t(xdrs, &objp->last_sno)) return (FALSE); if (!xdr_kdbe_time_t(xdrs, &objp->last_time)) return (FALSE); return (TRUE); } bool_t xdr_kdb_incr_result_t(xdrs, objp) XDR *xdrs; kdb_incr_result_t *objp; { rpc_inline_t *buf __unused; if (!xdr_kdb_last_t(xdrs, &objp->lastentry)) return (FALSE); if (!xdr_kdb_ulog_t(xdrs, &objp->updates)) return (FALSE); if (!xdr_update_status_t(xdrs, &objp->ret)) return (FALSE); return (TRUE); } bool_t xdr_kdb_fullresync_result_t(xdrs, objp) XDR *xdrs; kdb_fullresync_result_t *objp; { rpc_inline_t *buf __unused; if (!xdr_kdb_last_t(xdrs, &objp->lastentry)) return (FALSE); if (!xdr_update_status_t(xdrs, &objp->ret)) return (FALSE); return (TRUE); } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2006 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. */ /* * This code was based on code donated to MIT by Novell for * distribution under the MIT license. */ /* * Include files */ #include #include #include #include #include "kdb5.h" #include #include "k5-platform.h" #include /* Currently DB2 policy related errors are exported from DAL. But other databases should set_err function to return string. */ #include "adb_err.h" /* * Type definitions */ #define KRB5_TL_DB_ARGS 0x7fff /* * internal static variable */ static k5_mutex_t db_lock = K5_MUTEX_PARTIAL_INITIALIZER; #ifdef _KDB5_STATIC_LINK #undef _KDB5_DYNAMIC_LINK #else #undef _KDB5_DYNAMIC_LINK /* to avoid redefinition problem */ #define _KDB5_DYNAMIC_LINK #endif static db_library lib_list; /* * Helper Functions */ MAKE_INIT_FUNCTION(kdb_init_lock_list); MAKE_FINI_FUNCTION(kdb_fini_lock_list); int kdb_init_lock_list(void) { return k5_mutex_finish_init(&db_lock); } static int kdb_lock_list() { int err; err = CALL_INIT_FUNCTION (kdb_init_lock_list); if (err) return err; return k5_mutex_lock(&db_lock); } void kdb_fini_lock_list(void) { if (INITIALIZER_RAN(kdb_init_lock_list)) k5_mutex_destroy(&db_lock); } static int kdb_unlock_list() { return k5_mutex_unlock(&db_lock); } #define kdb_init_lib_lock(a) 0 #define kdb_destroy_lib_lock(a) (void)0 #define kdb_lock_lib_lock(a, b) 0 #define kdb_unlock_lib_lock(a, b) (void)0 /* Caller must free result*/ static char * kdb_get_conf_section(krb5_context kcontext) { krb5_error_code status = 0; char *result = NULL; char *value = NULL; if (kcontext->default_realm == NULL) return NULL; /* The profile has to have been initialized. If the profile was not initialized, expect nothing less than a crash. */ status = profile_get_string(kcontext->profile, /* realms */ KDB_REALM_SECTION, kcontext->default_realm, /* under the realm name, database_module */ KDB_MODULE_POINTER, /* default value is the realm name itself */ kcontext->default_realm, &value); if (status) { /* some problem */ result = strdup(kcontext->default_realm); /* let NULL be handled by the caller */ } else { result = strdup(value); /* free profile string */ profile_release_string(value); } return result; } static char * kdb_get_library_name(krb5_context kcontext) { krb5_error_code status = 0; char *result = NULL; char *value = NULL; char *lib = NULL; status = profile_get_string(kcontext->profile, /* realms */ KDB_REALM_SECTION, kcontext->default_realm, /* under the realm name, database_module */ KDB_MODULE_POINTER, /* default value is the realm name itself */ kcontext->default_realm, &value); if (status) { goto clean_n_exit; } #define DB2_NAME "db2" /* we got the module section. Get the library name from the module */ status = profile_get_string(kcontext->profile, KDB_MODULE_SECTION, value, KDB_LIB_POINTER, /* default to db2 */ DB2_NAME, &lib); if (status) { goto clean_n_exit; } result = strdup(lib); clean_n_exit: if (value) { /* free profile string */ profile_release_string(value); } if (lib) { /* free profile string */ profile_release_string(lib); } return result; } static void kdb_setup_opt_functions(db_library lib) { if (lib->vftabl.set_master_key == NULL) { lib->vftabl.set_master_key = kdb_def_set_mkey; } if (lib->vftabl.get_master_key == NULL) { lib->vftabl.get_master_key = kdb_def_get_mkey; } if (lib->vftabl.fetch_master_key == NULL) { lib->vftabl.fetch_master_key = krb5_db_def_fetch_mkey; } if (lib->vftabl.verify_master_key == NULL) { lib->vftabl.verify_master_key = krb5_def_verify_master_key; } if (lib->vftabl.dbe_search_enctype == NULL) { lib->vftabl.dbe_search_enctype = krb5_dbe_def_search_enctype; } if (lib->vftabl.db_change_pwd == NULL) { lib->vftabl.db_change_pwd = krb5_dbe_def_cpw; } if (lib->vftabl.store_master_key == NULL) { lib->vftabl.store_master_key = krb5_def_store_mkey; } if (lib->vftabl.promote_db == NULL) { lib->vftabl.promote_db = krb5_def_promote_db; } } static int kdb_db2_pol_err_loaded = 0; #ifdef _KDB5_STATIC_LINK #define DEF_SYMBOL(a) extern kdb_vftabl krb5_db_vftabl_ ## a #define GET_SYMBOL(a) (krb5_db_vftabl_ ## a) static krb5_error_code kdb_load_library(krb5_context kcontext, char *lib_name, db_library * lib) { krb5_error_code status; void *vftabl_addr = NULL; char buf[KRB5_MAX_ERR_STR]; if (!strcmp("kdb_db2", lib_name) && (kdb_db2_pol_err_loaded == 0)) { initialize_adb_error_table(); kdb_db2_pol_err_loaded = 1; } *lib = calloc((size_t) 1, sizeof(**lib)); if (*lib == NULL) { status = ENOMEM; goto clean_n_exit; } status = kdb_init_lib_lock(*lib); if (status) { goto clean_n_exit; } strcpy((*lib)->name, lib_name); #if !defined(KDB5_USE_LIB_KDB_DB2) && !defined(KDB5_USE_LIB_TEST) #error No database module defined #endif #ifdef KDB5_USE_LIB_KDB_DB2 if (strcmp(lib_name, "kdb_db2") == 0) { DEF_SYMBOL(kdb_db2); vftabl_addr = (void *) &GET_SYMBOL(kdb_db2); } else #endif #ifdef KDB5_USE_LIB_TEST if (strcmp(lib_name, "test") == 0) { DEF_SYMBOL(test); vftabl_addr = (void *) &GET_SYMBOL(test); } else #endif { snprintf(buf, sizeof(buf), gettext("Program not built to support %s database type\n"), lib_name); status = KRB5_KDB_DBTYPE_NOSUP; krb5_db_set_err(kcontext, krb5_err_have_str, status, buf); goto clean_n_exit; } memcpy(&(*lib)->vftabl, vftabl_addr, sizeof(kdb_vftabl)); kdb_setup_opt_functions(*lib); if ((status = (*lib)->vftabl.init_library())) { /* ERROR. library not initialized cleanly */ snprintf(buf, sizeof(buf), gettext("%s library initialization failed, error code %ld\n"), lib_name, status); status = KRB5_KDB_DBTYPE_INIT; krb5_db_set_err(kcontext, krb5_err_have_str, status, buf); goto clean_n_exit; } clean_n_exit: if (status) { free(*lib), *lib = NULL; } return status; } #else /* KDB5_STATIC_LINK*/ static char *db_dl_location[] = DEFAULT_KDB_LIB_PATH; #define db_dl_n_locations (sizeof(db_dl_location) / sizeof(db_dl_location[0])) static krb5_error_code kdb_load_library(krb5_context kcontext, char *lib_name, db_library * lib) { krb5_error_code status = 0; int ndx; void **vftabl_addrs = NULL; /* N.B.: If this is "const" but not "static", the Solaris 10 native compiler has trouble building the library because of absolute relocations needed in read-only section ".rodata". When it's static, it goes into ".picdata", which is read-write. */ static const char *const dbpath_names[] = { KDB_MODULE_SECTION, "db_module_dir", NULL, }; const char *filebases[2]; char **profpath = NULL; char **path = NULL; filebases[0] = lib_name; filebases[1] = NULL; if (!strcmp(DB2_NAME, lib_name) && (kdb_db2_pol_err_loaded == 0)) { initialize_adb_error_table(); kdb_db2_pol_err_loaded = 1; } *lib = calloc((size_t) 1, sizeof(**lib)); if (*lib == NULL) { status = ENOMEM; goto clean_n_exit; } status = kdb_init_lib_lock(*lib); if (status) { goto clean_n_exit; } strcpy((*lib)->name, lib_name); /* Fetch the list of directories specified in the config file(s) first. */ status = profile_get_values(kcontext->profile, dbpath_names, &profpath); if (status != 0 && status != PROF_NO_RELATION) goto clean_n_exit; ndx = 0; if (profpath) while (profpath[ndx] != NULL) ndx++; path = calloc(ndx + db_dl_n_locations, sizeof (char *)); if (path == NULL) { status = errno; goto clean_n_exit; } if (ndx) memcpy(path, profpath, ndx * sizeof(profpath[0])); memcpy(path + ndx, db_dl_location, db_dl_n_locations * sizeof(char *)); status = 0; if ((status = krb5int_open_plugin_dirs ((const char **) path, filebases, &(*lib)->dl_dir_handle, &kcontext->err))) { const char *err_str = krb5_get_error_message(kcontext, status); status = KRB5_KDB_DBTYPE_NOTFOUND; krb5_set_error_message (kcontext, status, gettext("Unable to find requested database type: %s"), err_str); krb5_free_error_message (kcontext, err_str); goto clean_n_exit; } if ((status = krb5int_get_plugin_dir_data (&(*lib)->dl_dir_handle, "kdb_function_table", &vftabl_addrs, &kcontext->err))) { const char *err_str = krb5_get_error_message(kcontext, status); status = KRB5_KDB_DBTYPE_INIT; krb5_set_error_message (kcontext, status, gettext("plugin symbol 'kdb_function_table' lookup failed: %s"), err_str); krb5_free_error_message (kcontext, err_str); goto clean_n_exit; } if (vftabl_addrs[0] == NULL) { /* No plugins! */ status = KRB5_KDB_DBTYPE_NOTFOUND; krb5_set_error_message (kcontext, status, gettext("Unable to load requested database module '%s': plugin symbol 'kdb_function_table' not found"), lib_name); goto clean_n_exit; } memcpy(&(*lib)->vftabl, vftabl_addrs[0], sizeof(kdb_vftabl)); kdb_setup_opt_functions(*lib); if ((status = (*lib)->vftabl.init_library())) { /* ERROR. library not initialized cleanly */ goto clean_n_exit; } clean_n_exit: if (vftabl_addrs != NULL) { krb5int_free_plugin_dir_data (vftabl_addrs); } /* Both of these DTRT with NULL. */ profile_free_list(profpath); free(path); if (status) { if (*lib) { kdb_destroy_lib_lock(*lib); if (PLUGIN_DIR_OPEN((&(*lib)->dl_dir_handle))) { krb5int_close_plugin_dirs (&(*lib)->dl_dir_handle); } free(*lib); *lib = NULL; } } return status; } #endif /* end of _KDB5_STATIC_LINK */ static krb5_error_code kdb_find_library(krb5_context kcontext, char *lib_name, db_library * lib) { /* lock here so that no two threads try to do the same at the same time */ krb5_error_code status = 0; int locked = 0; db_library curr_elt, prev_elt = NULL; if ((status = kdb_lock_list()) != 0) { goto clean_n_exit; } locked = 1; curr_elt = lib_list; while (curr_elt != NULL) { if (strcmp(lib_name, curr_elt->name) == 0) { *lib = curr_elt; goto clean_n_exit; } prev_elt = curr_elt; curr_elt = curr_elt->next; } /* module not found. create and add to list */ status = kdb_load_library(kcontext, lib_name, lib); if (status) { goto clean_n_exit; } if (prev_elt) { /* prev_elt points to the last element in the list */ prev_elt->next = *lib; (*lib)->prev = prev_elt; } else { lib_list = *lib; } clean_n_exit: if (*lib) { (*lib)->reference_cnt++; } if (locked) { (void)kdb_unlock_list(); } return status; } static krb5_error_code kdb_free_library(db_library lib) { krb5_error_code status = 0; int locked = 0; if ((status = kdb_lock_list()) != 0) { goto clean_n_exit; } locked = 1; lib->reference_cnt--; if (lib->reference_cnt == 0) { status = lib->vftabl.fini_library(); if (status) { goto clean_n_exit; } /* close the library */ if (PLUGIN_DIR_OPEN((&lib->dl_dir_handle))) { krb5int_close_plugin_dirs (&lib->dl_dir_handle); } kdb_destroy_lib_lock(lib); if (lib->prev == NULL) { /* first element in the list */ lib_list = lib->next; } else { lib->prev->next = lib->next; } if (lib->next) { lib->next->prev = lib->prev; } free(lib); } clean_n_exit: if (locked) { (void)kdb_unlock_list(); } return status; } static krb5_error_code kdb_setup_lib_handle(krb5_context kcontext) { char *library = NULL; krb5_error_code status = 0; db_library lib = NULL; kdb5_dal_handle *dal_handle = NULL; dal_handle = calloc((size_t) 1, sizeof(kdb5_dal_handle)); if (dal_handle == NULL) { status = ENOMEM; goto clean_n_exit; } library = kdb_get_library_name(kcontext); if (library == NULL) { status = KRB5_KDB_DBTYPE_NOTFOUND; goto clean_n_exit; } status = kdb_find_library(kcontext, library, &lib); if (status) { goto clean_n_exit; } dal_handle->lib_handle = lib; kcontext->db_context = (void *) dal_handle; clean_n_exit: free(library); if (status) { free(dal_handle); if (lib) { (void)kdb_free_library(lib); } } return status; } static krb5_error_code kdb_free_lib_handle(krb5_context kcontext) { krb5_error_code status = 0; status = kdb_free_library(((kdb5_dal_handle *) kcontext->db_context)-> lib_handle); if (status) { goto clean_n_exit; } free(kcontext->db_context); kcontext->db_context = NULL; clean_n_exit: return status; } static void get_errmsg (krb5_context kcontext, krb5_error_code err_code) { kdb5_dal_handle *dal_handle; const char *e; if (err_code == 0) return; assert(kcontext != NULL); /* Must be called with dal_handle->lib_handle locked! */ assert(kcontext->db_context != NULL); dal_handle = (kdb5_dal_handle *) kcontext->db_context; if (dal_handle->lib_handle->vftabl.errcode_2_string == NULL) return; e = dal_handle->lib_handle->vftabl.errcode_2_string(kcontext, err_code); assert (e != NULL); krb5_set_error_message(kcontext, err_code, "%s", e); if (dal_handle->lib_handle->vftabl.release_errcode_string) dal_handle->lib_handle->vftabl.release_errcode_string(kcontext, e); } /* * External functions... DAL API */ krb5_error_code krb5_db_open(krb5_context kcontext, char **db_args, int mode) { krb5_error_code status = 0; char *section = NULL; kdb5_dal_handle *dal_handle; section = kdb_get_conf_section(kcontext); if (section == NULL) { status = KRB5_KDB_SERVER_INTERNAL_ERR; krb5_set_error_message (kcontext, status, gettext("unable to determine configuration section for realm %s\n"), kcontext->default_realm ? kcontext->default_realm : "[UNSET]"); goto clean_n_exit; } if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { /* Solaris Kerberos */ kdb_free_lib_handle(kcontext); goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.init_module(kcontext, section, db_args, mode); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); /* Solaris Kerberos */ if (status) kdb_free_lib_handle(kcontext); clean_n_exit: if (section) free(section); return status; } krb5_error_code krb5_db_inited(krb5_context kcontext) { return !(kcontext && kcontext->db_context && ((kdb5_dal_handle *) kcontext->db_context)->db_context); } krb5_error_code krb5_db_create(krb5_context kcontext, char **db_args) { krb5_error_code status = 0; char *section = NULL; kdb5_dal_handle *dal_handle; section = kdb_get_conf_section(kcontext); if (section == NULL) { status = KRB5_KDB_SERVER_INTERNAL_ERR; krb5_set_error_message (kcontext, status, gettext("unable to determine configuration section for realm %s\n"), kcontext->default_realm); goto clean_n_exit; } if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_create(kcontext, section, db_args); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: if (section) free(section); return status; } krb5_error_code krb5_db_fini(krb5_context kcontext) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { /* module not loaded. So nothing to be done */ goto clean_n_exit; } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.fini_module(kcontext); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = kdb_free_lib_handle(kcontext); clean_n_exit: return status; } krb5_error_code krb5_db_destroy(krb5_context kcontext, char **db_args) { krb5_error_code status = 0; char *section = NULL; kdb5_dal_handle *dal_handle; section = kdb_get_conf_section(kcontext); if (section == NULL) { status = KRB5_KDB_SERVER_INTERNAL_ERR; krb5_set_error_message (kcontext, status, gettext("unable to determine configuration section for realm %s\n"), kcontext->default_realm); goto clean_n_exit; } if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_destroy(kcontext, section, db_args); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: if (section) free(section); return status; } krb5_error_code krb5_db_get_age(krb5_context kcontext, char *db_name, time_t * t) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_get_age(kcontext, db_name, t); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_set_option(krb5_context kcontext, int option, void *value) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_set_option(kcontext, option, value); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_lock(krb5_context kcontext, int lock_mode) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; /* acquire an exclusive lock, ensures no other thread uses this context */ status = kdb_lock_lib_lock(dal_handle->lib_handle, TRUE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_lock(kcontext, lock_mode); get_errmsg(kcontext, status); /* exclusive lock is still held, so no other thread could use this context */ kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_unlock(krb5_context kcontext) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; /* normal lock acquired and exclusive lock released */ status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_unlock(kcontext); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, TRUE); clean_n_exit: return status; } krb5_error_code krb5_db_get_principal(krb5_context kcontext, krb5_const_principal search_for, krb5_db_entry * entries, int *nentries, krb5_boolean * more) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_get_principal(kcontext, search_for, entries, nentries, more); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_get_principal_nolock(krb5_context kcontext, krb5_const_principal search_for, krb5_db_entry * entries, int *nentries, krb5_boolean * more) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_get_principal_nolock(kcontext, search_for, entries, nentries, more); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_free_principal(krb5_context kcontext, krb5_db_entry * entry, int count) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_free_principal(kcontext, entry, count); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_put_principal(krb5_context kcontext, krb5_db_entry * entries, int *nentries) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; char **db_args = NULL; krb5_tl_data *prev, *curr, *next; int db_args_size = 0; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } /* Giving db_args as part of tl data causes, db2 to store the tl_data as such. To prevent this, tl_data is collated and passed as a sepearte argument. Currently supports only one principal. but passing it as a seperate argument makes it difficult for kadmin remote to pass arguments to server. */ prev = NULL, curr = entries->tl_data; while (curr) { if (curr->tl_data_type == KRB5_TL_DB_ARGS) { char **t; /* Since this is expected to be NULL terminated string and this could come from any client, do a check before passing it to db. */ if (((char *) curr->tl_data_contents)[curr->tl_data_length - 1] != '\0') { /* not null terminated. Dangerous input */ status = EINVAL; goto clean_n_exit; } db_args_size++; t = realloc(db_args, sizeof(char *) * (db_args_size + 1)); /* 1 for NULL */ if (t == NULL) { status = ENOMEM; goto clean_n_exit; } db_args = t; db_args[db_args_size - 1] = (char *) curr->tl_data_contents; db_args[db_args_size] = NULL; next = curr->tl_data_next; if (prev == NULL) { /* current node is the first in the linked list. remove it */ entries->tl_data = curr->tl_data_next; } else { prev->tl_data_next = curr->tl_data_next; } entries->n_tl_data--; krb5_db_free(kcontext, curr); /* previous does not change */ curr = next; } else { prev = curr; curr = curr->tl_data_next; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_put_principal(kcontext, entries, nentries, db_args); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: while (db_args_size) { if (db_args[db_args_size - 1]) krb5_db_free(kcontext, db_args[db_args_size - 1]); db_args_size--; } if (db_args) free(db_args); return status; } krb5_error_code krb5_db_delete_principal(krb5_context kcontext, krb5_principal search_for, int *nentries) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_delete_principal(kcontext, search_for, nentries); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_iterate(krb5_context kcontext, char *match_entry, int (*func) (krb5_pointer, krb5_db_entry *), krb5_pointer func_arg, /* Solaris Kerberos: adding support for db_args */ char **db_args) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } /* Solaris Kerberos: adding support for db_args */ status = dal_handle->lib_handle->vftabl.db_iterate(kcontext, match_entry, func, func_arg, db_args); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_supported_realms(krb5_context kcontext, char **realms) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_supported_realms(kcontext, realms); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_free_supported_realms(krb5_context kcontext, char **realms) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_free_supported_realms(kcontext, realms); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_set_master_key_ext(krb5_context kcontext, char *pwd, krb5_keyblock * key) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.set_master_key(kcontext, pwd, key); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_set_mkey(krb5_context context, krb5_keyblock * key) { return krb5_db_set_master_key_ext(context, NULL, key); } krb5_error_code krb5_db_get_mkey(krb5_context kcontext, krb5_keyblock ** key) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } /* Lets use temp key and copy it later to avoid memory problems when freed by the caller. */ status = dal_handle->lib_handle->vftabl.get_master_key(kcontext, key); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_store_master_key(krb5_context kcontext, char *db_arg, krb5_principal mname, krb5_keyblock * key, char *master_pwd) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.store_master_key(kcontext, db_arg, mname, key, master_pwd); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } char *krb5_mkey_pwd_prompt1 = KRB5_KDC_MKEY_1; char *krb5_mkey_pwd_prompt2 = KRB5_KDC_MKEY_2; krb5_error_code krb5_db_fetch_mkey(krb5_context context, krb5_principal mname, krb5_enctype etype, krb5_boolean fromkeyboard, krb5_boolean twice, char *db_args, krb5_data * salt, krb5_keyblock * key) { krb5_error_code retval; char password[BUFSIZ]; krb5_data pwd; unsigned int size = sizeof(password); int kvno; krb5_keyblock tmp_key; memset(&tmp_key, 0, sizeof(tmp_key)); if (fromkeyboard) { krb5_data scratch; if ((retval = krb5_read_password(context, krb5_mkey_pwd_prompt1, twice ? krb5_mkey_pwd_prompt2 : 0, password, &size))) { goto clean_n_exit; } pwd.data = password; pwd.length = size; if (!salt) { retval = krb5_principal2salt(context, mname, &scratch); if (retval) goto clean_n_exit; } retval = krb5_c_string_to_key(context, etype, &pwd, salt ? salt : &scratch, key); if (!salt) krb5_xfree(scratch.data); memset(password, 0, sizeof(password)); /* erase it */ } else { kdb5_dal_handle *dal_handle; if (context->db_context == NULL) { retval = kdb_setup_lib_handle(context); if (retval) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) context->db_context; retval = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (retval) { goto clean_n_exit; } #if 0 /************** Begin IFDEF'ed OUT *******************************/ /* Orig MIT */ tmp_key.enctype = key->enctype; #else /* Solaris Kerberos: need to use etype */ tmp_key.enctype = etype; #endif /**************** END IFDEF'ed OUT *******************************/ retval = dal_handle->lib_handle->vftabl.fetch_master_key(context, mname, &tmp_key, &kvno, db_args); get_errmsg(context, retval); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); if (retval) { goto clean_n_exit; } key->contents = malloc(tmp_key.length); if (key->contents == NULL) { retval = ENOMEM; goto clean_n_exit; } key->magic = tmp_key.magic; key->enctype = tmp_key.enctype; key->length = tmp_key.length; memcpy(key->contents, tmp_key.contents, tmp_key.length); } clean_n_exit: if (tmp_key.contents) { memset(tmp_key.contents, 0, tmp_key.length); krb5_db_free(context, tmp_key.contents); } return retval; } krb5_error_code krb5_db_verify_master_key(krb5_context kcontext, krb5_principal mprinc, krb5_keyblock * mkey) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.verify_master_key(kcontext, mprinc, mkey); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } void * krb5_db_alloc(krb5_context kcontext, void *ptr, size_t size) { krb5_error_code status; kdb5_dal_handle *dal_handle; void *new_ptr = NULL; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; new_ptr = dal_handle->lib_handle->vftabl.db_alloc(kcontext, ptr, size); clean_n_exit: return new_ptr; } void krb5_db_free(krb5_context kcontext, void *ptr) { krb5_error_code status; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; dal_handle->lib_handle->vftabl.db_free(kcontext, ptr); clean_n_exit: return; } /* has to be modified */ krb5_error_code krb5_dbe_find_enctype(krb5_context kcontext, krb5_db_entry * dbentp, krb5_int32 ktype, krb5_int32 stype, krb5_int32 kvno, krb5_key_data ** kdatap) { krb5_int32 start = 0; return krb5_dbe_search_enctype(kcontext, dbentp, &start, ktype, stype, kvno, kdatap); } krb5_error_code krb5_dbe_search_enctype(krb5_context kcontext, krb5_db_entry * dbentp, krb5_int32 * start, krb5_int32 ktype, krb5_int32 stype, krb5_int32 kvno, krb5_key_data ** kdatap) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.dbe_search_enctype(kcontext, dbentp, start, ktype, stype, kvno, kdatap); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } #define REALM_SEP_STRING "@" krb5_error_code krb5_db_setup_mkey_name(krb5_context context, const char *keyname, const char *realm, char **fullname, krb5_principal * principal) { krb5_error_code retval; size_t keylen; size_t rlen = strlen(realm); char *fname; if (!keyname) keyname = KRB5_KDB_M_NAME; /* XXX external? */ keylen = strlen(keyname); fname = malloc(keylen + rlen + strlen(REALM_SEP_STRING) + 1); if (!fname) return ENOMEM; strcpy(fname, keyname); (void)strcat(fname, REALM_SEP_STRING); (void)strcat(fname, realm); if ((retval = krb5_parse_name(context, fname, principal))) return retval; if (fullname) *fullname = fname; else free(fname); return 0; } krb5_error_code krb5_dbe_lookup_last_pwd_change(context, entry, stamp) krb5_context context; krb5_db_entry *entry; krb5_timestamp *stamp; { krb5_tl_data tl_data; krb5_error_code code; krb5_int32 tmp; tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE; if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data))) return (code); if (tl_data.tl_data_length != 4) { *stamp = 0; return (0); } krb5_kdb_decode_int32(tl_data.tl_data_contents, tmp); *stamp = (krb5_timestamp) tmp; return (0); } /*ARGSUSED*/ krb5_error_code krb5_dbe_lookup_tl_data(context, entry, ret_tl_data) krb5_context context; krb5_db_entry *entry; krb5_tl_data *ret_tl_data; { krb5_tl_data *tl_data; for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) { if (tl_data->tl_data_type == ret_tl_data->tl_data_type) { *ret_tl_data = *tl_data; return (0); } } /* if the requested record isn't found, return zero bytes. * if it ever means something to have a zero-length tl_data, * this code and its callers will have to be changed */ ret_tl_data->tl_data_length = 0; ret_tl_data->tl_data_contents = NULL; return (0); } krb5_error_code krb5_dbe_create_key_data(context, entry) krb5_context context; krb5_db_entry *entry; { if ((entry->key_data = (krb5_key_data *) krb5_db_alloc(context, entry->key_data, (sizeof(krb5_key_data) * (entry->n_key_data + 1)))) == NULL) return (ENOMEM); memset(entry->key_data + entry->n_key_data, 0, sizeof(krb5_key_data)); entry->n_key_data++; return 0; } krb5_error_code krb5_dbe_update_mod_princ_data(context, entry, mod_date, mod_princ) krb5_context context; krb5_db_entry *entry; krb5_timestamp mod_date; krb5_const_principal mod_princ; { krb5_tl_data tl_data; krb5_error_code retval = 0; krb5_octet *nextloc = 0; char *unparse_mod_princ = 0; unsigned int unparse_mod_princ_size; if ((retval = krb5_unparse_name(context, mod_princ, &unparse_mod_princ))) return (retval); unparse_mod_princ_size = strlen(unparse_mod_princ) + 1; if ((nextloc = (krb5_octet *) malloc(unparse_mod_princ_size + 4)) == NULL) { free(unparse_mod_princ); return (ENOMEM); } tl_data.tl_data_type = KRB5_TL_MOD_PRINC; tl_data.tl_data_length = unparse_mod_princ_size + 4; tl_data.tl_data_contents = nextloc; /* Mod Date */ krb5_kdb_encode_int32(mod_date, nextloc); /* Mod Princ */ memcpy(nextloc + 4, unparse_mod_princ, unparse_mod_princ_size); retval = krb5_dbe_update_tl_data(context, entry, &tl_data); free(unparse_mod_princ); free(nextloc); return (retval); } krb5_error_code krb5_dbe_lookup_mod_princ_data(context, entry, mod_time, mod_princ) krb5_context context; krb5_db_entry *entry; krb5_timestamp *mod_time; krb5_principal *mod_princ; { krb5_tl_data tl_data; krb5_error_code code; tl_data.tl_data_type = KRB5_TL_MOD_PRINC; if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data))) return (code); if ((tl_data.tl_data_length < 5) || (tl_data.tl_data_contents[tl_data.tl_data_length - 1] != '\0')) return (KRB5_KDB_TRUNCATED_RECORD); /* Mod Date */ krb5_kdb_decode_int32(tl_data.tl_data_contents, *mod_time); /* Mod Princ */ if ((code = krb5_parse_name(context, (const char *) (tl_data.tl_data_contents + 4), mod_princ))) return (code); return (0); } krb5_error_code krb5_dbe_update_last_pwd_change(context, entry, stamp) krb5_context context; krb5_db_entry *entry; krb5_timestamp stamp; { krb5_tl_data tl_data; krb5_octet buf[4]; /* this is the encoded size of an int32 */ tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE; tl_data.tl_data_length = sizeof(buf); krb5_kdb_encode_int32((krb5_int32) stamp, buf); tl_data.tl_data_contents = buf; return (krb5_dbe_update_tl_data(context, entry, &tl_data)); } krb5_error_code krb5_dbe_update_tl_data(context, entry, new_tl_data) krb5_context context; krb5_db_entry *entry; krb5_tl_data *new_tl_data; { krb5_tl_data *tl_data = NULL; krb5_octet *tmp; /* copy the new data first, so we can fail cleanly if malloc() * fails */ if ((tmp = (krb5_octet *) krb5_db_alloc(context, NULL, new_tl_data->tl_data_length)) == NULL) return (ENOMEM); /* Find an existing entry of the specified type and point at * it, or NULL if not found */ if (new_tl_data->tl_data_type != KRB5_TL_DB_ARGS) { /* db_args can be multiple */ for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) if (tl_data->tl_data_type == new_tl_data->tl_data_type) break; } /* if necessary, chain a new record in the beginning and point at it */ if (!tl_data) { if ((tl_data = (krb5_tl_data *) krb5_db_alloc(context, NULL, sizeof(krb5_tl_data))) == NULL) { free(tmp); return (ENOMEM); } memset(tl_data, 0, sizeof(krb5_tl_data)); tl_data->tl_data_next = entry->tl_data; entry->tl_data = tl_data; entry->n_tl_data++; } /* fill in the record */ if (tl_data->tl_data_contents) krb5_db_free(context, tl_data->tl_data_contents); tl_data->tl_data_type = new_tl_data->tl_data_type; tl_data->tl_data_length = new_tl_data->tl_data_length; tl_data->tl_data_contents = tmp; memcpy(tmp, new_tl_data->tl_data_contents, tl_data->tl_data_length); return (0); } /* change password functions */ krb5_error_code krb5_dbe_cpw(krb5_context kcontext, krb5_keyblock * master_key, krb5_key_salt_tuple * ks_tuple, int ks_tuple_count, char *passwd, int new_kvno, krb5_boolean keepold, krb5_db_entry * db_entry) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_change_pwd(kcontext, master_key, ks_tuple, ks_tuple_count, passwd, new_kvno, keepold, db_entry); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } /* policy management functions */ krb5_error_code krb5_db_create_policy(krb5_context kcontext, osa_policy_ent_t policy) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_create_policy(kcontext, policy); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_get_policy(krb5_context kcontext, char *name, osa_policy_ent_t * policy, int *cnt) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_get_policy(kcontext, name, policy, cnt); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_put_policy(krb5_context kcontext, osa_policy_ent_t policy) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_put_policy(kcontext, policy); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_iter_policy(krb5_context kcontext, char *match_entry, osa_adb_iter_policy_func func, void *data) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_iter_policy(kcontext, match_entry, func, data); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } krb5_error_code krb5_db_delete_policy(krb5_context kcontext, char *policy) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.db_delete_policy(kcontext, policy); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } void krb5_db_free_policy(krb5_context kcontext, osa_policy_ent_t policy) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } dal_handle->lib_handle->vftabl.db_free_policy(kcontext, policy); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return; } krb5_error_code krb5_db_promote(krb5_context kcontext, char **db_args) { krb5_error_code status = 0; char *section = NULL; kdb5_dal_handle *dal_handle; section = kdb_get_conf_section(kcontext); if (section == NULL) { status = KRB5_KDB_SERVER_INTERNAL_ERR; krb5_set_error_message (kcontext, status, gettext("unable to determine configuration section for realm %s\n"), kcontext->default_realm); goto clean_n_exit; } if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } status = dal_handle->lib_handle->vftabl.promote_db(kcontext, section, db_args); get_errmsg(kcontext, status); kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: if (section) free(section); return status; } /* * Solaris Kerberos: support for iprop * * Not all KDB plugins support iprop. * * sets iprop_supported to 1 if iprop supportd, 0 otherwise. */ krb5_error_code krb5_db_supports_iprop(krb5_context kcontext, int *iprop_supported) { krb5_error_code status = 0; kdb5_dal_handle *dal_handle; if (kcontext->db_context == NULL) { status = kdb_setup_lib_handle(kcontext); if (status) { goto clean_n_exit; } } dal_handle = (kdb5_dal_handle *) kcontext->db_context; status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE); if (status) { goto clean_n_exit; } *iprop_supported = dal_handle->lib_handle->vftabl.iprop_supported; kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE); clean_n_exit: return status; } #ifndef _KRB5_KDB5_H_ #define _KRB5_KDB5_H_ #if HAVE_UNISTD_H #include #endif #include #include #include #include #include "kdb.h" #define KDB_MAX_DB_NAME 128 #define KDB_REALM_SECTION "realms" #define KDB_MODULE_POINTER "database_module" #define KDB_MODULE_DEF_SECTION "dbdefaults" #define KDB_MODULE_SECTION "dbmodules" #define KDB_LIB_POINTER "db_library" #define KDB_DATABASE_CONF_FILE DEFAULT_SECURE_PROFILE_PATH #define KDB_DATABASE_ENV_PROF KDC_PROFILE_ENV #define KRB5_DB_GET_DB_CONTEXT(kcontext) (((kdb5_dal_handle*) (kcontext)->db_context)->db_context) #define KRB5_DB_GET_PROFILE(kcontext) ((kcontext)->profile) #define KRB5_DB_GET_REALM(kcontext) ((kcontext)->default_realm) typedef struct _kdb_vftabl{ short int maj_ver; short int min_ver; int iprop_supported; krb5_error_code (*init_library)(); krb5_error_code (*fini_library)(); krb5_error_code (*init_module) (krb5_context kcontext, char * conf_section, char ** db_args, int mode); krb5_error_code (*fini_module) (krb5_context kcontext); krb5_error_code (*db_create) (krb5_context kcontext, char * conf_section, char ** db_args); krb5_error_code (*db_destroy) (krb5_context kcontext, char *conf_section, char ** db_args); krb5_error_code (*db_get_age) (krb5_context kcontext, char *db_name, time_t *age); krb5_error_code (*db_set_option) (krb5_context kcontext, int option, void *value); krb5_error_code (*db_lock) (krb5_context kcontext, int mode); krb5_error_code (*db_unlock) (krb5_context kcontext); krb5_error_code (*db_get_principal) (krb5_context kcontext, krb5_const_principal search_for, krb5_db_entry *entries, int *nentries, krb5_boolean *more); krb5_error_code (*db_get_principal_nolock) (krb5_context kcontext, krb5_const_principal search_for, krb5_db_entry *entries, int *nentries, krb5_boolean *more); krb5_error_code (*db_free_principal) (krb5_context kcontext, krb5_db_entry *entry, int count); krb5_error_code (*db_put_principal) (krb5_context kcontext, krb5_db_entry *entries, int *nentries, char **db_args); krb5_error_code (*db_delete_principal) (krb5_context kcontext, krb5_const_principal search_for, int *nentries); /* Solaris Kerberos: adding support for db_args */ krb5_error_code (*db_iterate) (krb5_context kcontext, char *match_entry, int (*func) (krb5_pointer, krb5_db_entry *), krb5_pointer func_arg, char **db_args); krb5_error_code (*db_create_policy) (krb5_context kcontext, osa_policy_ent_t policy); krb5_error_code (*db_get_policy) (krb5_context kcontext, char *name, osa_policy_ent_t *policy, int *cnt); krb5_error_code (*db_put_policy) (krb5_context kcontext, osa_policy_ent_t policy); krb5_error_code (*db_iter_policy) (krb5_context kcontext, char *match_entry, osa_adb_iter_policy_func func, void *data); krb5_error_code (*db_delete_policy) (krb5_context kcontext, char *policy); void (*db_free_policy) (krb5_context kcontext, osa_policy_ent_t val); krb5_error_code (*db_supported_realms) (krb5_context kcontext, char **realms); krb5_error_code (*db_free_supported_realms) (krb5_context kcontext, char **realms); const char * (*errcode_2_string) (krb5_context kcontext, long err_code); void (*release_errcode_string) (krb5_context kcontext, const char *msg); void * (*db_alloc) (krb5_context kcontext, void *ptr, size_t size); void (*db_free) (krb5_context kcontext, void *ptr); /* optional functions */ krb5_error_code (*set_master_key) (krb5_context kcontext, char *pwd, krb5_keyblock *key); krb5_error_code (*get_master_key) (krb5_context kcontext, krb5_keyblock **key); krb5_error_code (*setup_master_key_name) (krb5_context kcontext, char *keyname, char *realm, char **fullname, krb5_principal *principal); krb5_error_code (*store_master_key) (krb5_context kcontext, char *db_arg, krb5_principal mname, krb5_keyblock *key, char *master_pwd); krb5_error_code (*fetch_master_key) (krb5_context kcontext, krb5_principal mname, krb5_keyblock *key, int *kvno, char *db_args); krb5_error_code (*verify_master_key) (krb5_context kcontext, krb5_principal mprinc, krb5_keyblock *mkey); krb5_error_code (*dbe_search_enctype) (krb5_context kcontext, krb5_db_entry *dbentp, krb5_int32 *start, krb5_int32 ktype, krb5_int32 stype, krb5_int32 kvno, krb5_key_data **kdatap); krb5_error_code (*db_change_pwd) (krb5_context context, krb5_keyblock * master_key, krb5_key_salt_tuple * ks_tuple, int ks_tuple_count, char * passwd, int new_kvno, krb5_boolean keepold, krb5_db_entry * db_entry); /* Promote a temporary database to be the live one. */ krb5_error_code (*promote_db) (krb5_context context, char *conf_section, char **db_args); } kdb_vftabl; typedef struct _db_library { char name[KDB_MAX_DB_NAME]; int reference_cnt; struct plugin_dir_handle dl_dir_handle; kdb_vftabl vftabl; struct _db_library *next, *prev; } *db_library; typedef struct _kdb5_dal_handle { /* Helps us to change db_library without affecting modules to some extent. */ void *db_context; db_library lib_handle; } kdb5_dal_handle; #endif /* end of _KRB5_KDB5_H_ */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This file contains api's for conversion of the kdb_incr_update_t * struct(s) into krb5_db_entry struct(s) and vice-versa. */ #include #include #include #include #include #include "iprop.h" #include #include #include /* BEGIN CSTYLED */ #define ULOG_ENTRY_TYPE(upd, i) ((kdb_incr_update_t *)upd)->kdb_update.kdbe_t_val[i] #define ULOG_ENTRY(upd, i) ((kdb_incr_update_t *)upd)->kdb_update.kdbe_t_val[i].kdbe_val_t_u #define ULOG_ENTRY_KEYVAL(upd, i, j) ((kdb_incr_update_t *)upd)->kdb_update.kdbe_t_val[i].kdbe_val_t_u.av_keydata.av_keydata_val[j] #define ULOG_ENTRY_PRINC(upd, i, j) ((kdb_incr_update_t *)upd)->kdb_update.kdbe_t_val[i].kdbe_val_t_u.av_princ.k_components.k_components_val[j] #define ULOG_ENTRY_MOD_PRINC(upd, i, j) ((kdb_incr_update_t *)upd)->kdb_update.kdbe_t_val[i].kdbe_val_t_u.av_mod_princ.k_components.k_components_val[j] /* END CSTYLED */ typedef enum { REG_PRINC = 0, MOD_PRINC = 1 } princ_type; /* * This routine tracks the krb5_db_entry fields that have been modified * (by comparing it to the db_entry currently present in principal.db) * in the update. */ static void find_changed_attrs(krb5_db_entry *current, krb5_db_entry *new, kdbe_attr_type_t *attrs, int *nattrs) { int i = 0, j = 0; krb5_tl_data *first, *second; if (current->attributes != new->attributes) attrs[i++] = AT_ATTRFLAGS; if (current->max_life != new->max_life) attrs[i++] = AT_MAX_LIFE; if (current->max_renewable_life != new->max_renewable_life) attrs[i++] = AT_MAX_RENEW_LIFE; if (current->expiration != new->expiration) attrs[i++] = AT_EXP; if (current->pw_expiration != new->pw_expiration) attrs[i++] = AT_PW_EXP; if (current->last_success != new->last_success) attrs[i++] = AT_LAST_SUCCESS; if (current->last_failed != new->last_failed) attrs[i++] = AT_LAST_FAILED; if (current->fail_auth_count != new->fail_auth_count) attrs[i++] = AT_FAIL_AUTH_COUNT; if ((current->princ->type == new->princ->type) && (current->princ->length == new->princ->length)) { if ((current->princ->realm.length == new->princ->realm.length) && strncmp(current->princ->realm.data, new->princ->realm.data, current->princ->realm.length)) { for (j = 0; j < current->princ->length; j++) { if ((current->princ->data[j].data != NULL) && (strncmp(current->princ->data[j].data, new->princ->data[j].data, current->princ->data[j].length))) { attrs[i++] = AT_PRINC; break; } } } else { attrs[i++] = AT_PRINC; } } else { attrs[i++] = AT_PRINC; } if (current->n_key_data == new->n_key_data) { /* Assuming key ordering is the same in new & current */ for (j = 0; j < new->n_key_data; j++) { if (current->key_data[j].key_data_kvno != new->key_data[j].key_data_kvno) { attrs[i++] = AT_KEYDATA; break; } } } else { attrs[i++] = AT_KEYDATA; } if (current->n_tl_data == new->n_tl_data) { /* Assuming we preserve the TL_DATA ordering between updates */ for (first = current->tl_data, second = new->tl_data; first; first = first->tl_data_next, second = second->tl_data_next) { if ((first->tl_data_length == second->tl_data_length) && (first->tl_data_type == second->tl_data_type)) { if ((memcmp((char *)first->tl_data_contents, (char *)second->tl_data_contents, first->tl_data_length)) != 0) { attrs[i++] = AT_TL_DATA; break; } } else { attrs[i++] = AT_TL_DATA; break; } } } else { attrs[i++] = AT_TL_DATA; } if (current->len != new->len) attrs[i++] = AT_LEN; /* * Store the no. of (possibly :)) changed attributes */ *nattrs = i; } static int data_to_utf8str(utf8str_t *u, krb5_data d) { u->utf8str_t_len = d.length; if (d.data) { u->utf8str_t_val = malloc(d.length); if (u->utf8str_t_val == NULL) return -1; memcpy(u->utf8str_t_val, d.data, d.length); } else u->utf8str_t_val = NULL; return 0; } /* * Converts the krb5_principal struct from db2 to ulog format. */ static krb5_error_code conv_princ_2ulog(krb5_principal princ, kdb_incr_update_t *upd, int cnt, princ_type tp) { int i = 0; kdbe_princ_t *p; kdbe_data_t *components; if ((upd == NULL) || !princ) return (KRB5KRB_ERR_GENERIC); switch (tp) { case REG_PRINC: case MOD_PRINC: p = &ULOG_ENTRY(upd, cnt).av_princ; /* or av_mod_princ */ p->k_nametype = (int32_t)princ->type; if (data_to_utf8str(&p->k_realm, princ->realm) < 0) { return ENOMEM; } p->k_components.k_components_len = princ->length; p->k_components.k_components_val = components = malloc(princ->length * sizeof (kdbe_data_t)); if (p->k_components.k_components_val == NULL) { free(p->k_realm.utf8str_t_val); p->k_realm.utf8str_t_val = NULL; return (ENOMEM); } memset(components, 0, princ->length * sizeof(kdbe_data_t)); for (i = 0; i < princ->length; i++) components[i].k_data.utf8str_t_val = NULL; for (i = 0; i < princ->length; i++) { components[i].k_magic = princ->data[i].magic; if (data_to_utf8str(&components[i].k_data, princ->data[i]) < 0) { int j; for (j = 0; j < i; j++) { free(components[j].k_data.utf8str_t_val); components[j].k_data.utf8str_t_val = NULL; } free(components); p->k_components.k_components_val = NULL; free(p->k_realm.utf8str_t_val); p->k_realm.utf8str_t_val = NULL; return ENOMEM; } } break; default: break; } return (0); } /* * Copies a UTF-8 string from ulog to a krb5_data object, which may * already have allocated storage associated with it. * * Maybe a return value should indicate success/failure? */ static void set_from_utf8str(krb5_data *d, utf8str_t u) { if (u.utf8str_t_len > INT_MAX-1 || u.utf8str_t_len >= SIZE_MAX-1) { d->data = NULL; return; } d->length = u.utf8str_t_len; d->data = malloc(d->length + 1); if (d->data == NULL) return; if (d->length) /* Pointer may be null if length = 0. */ strncpy(d->data, u.utf8str_t_val, d->length); d->data[d->length] = 0; } /* * Converts the krb5_principal struct from ulog to db2 format. */ static krb5_principal conv_princ_2db(krb5_context context, kdbe_princ_t *kdbe_princ) { int i; krb5_principal princ; kdbe_data_t *components; princ = calloc(1, sizeof (krb5_principal_data)); if (princ == NULL) { return NULL; } princ->length = 0; princ->data = NULL; components = kdbe_princ->k_components.k_components_val; princ->type = (krb5_int32) kdbe_princ->k_nametype; princ->realm.data = NULL; set_from_utf8str(&princ->realm, kdbe_princ->k_realm); if (princ->realm.data == NULL) goto error; princ->data = calloc(kdbe_princ->k_components.k_components_len, sizeof (krb5_data)); if (princ->data == NULL) goto error; for (i = 0; i < kdbe_princ->k_components.k_components_len; i++) princ->data[i].data = NULL; princ->length = (krb5_int32)kdbe_princ->k_components.k_components_len; for (i = 0; i < princ->length; i++) { princ->data[i].magic = components[i].k_magic; set_from_utf8str(&princ->data[i], components[i].k_data); if (princ->data[i].data == NULL) goto error; } return princ; error: krb5_free_principal(context, princ); return NULL; } /* * This routine converts one or more krb5 db2 records into update * log (ulog) entry format. Space for the update log entries should * be allocated prior to invocation of this routine. */ krb5_error_code ulog_conv_2logentry(krb5_context context, krb5_db_entry *entries, kdb_incr_update_t *updates, int nentries) { int i, j, k, cnt, final, nattrs, tmpint, nprincs; unsigned int more; krb5_principal tmpprinc; krb5_tl_data *newtl; krb5_db_entry curr; krb5_error_code ret; kdbe_attr_type_t *attr_types; kdb_incr_update_t *upd; krb5_db_entry *ent; int kadm_data_yes; if ((updates == NULL) || (entries == NULL)) return (KRB5KRB_ERR_GENERIC); upd = updates; ent = entries; for (k = 0; k < nentries; k++) { nprincs = nattrs = tmpint = 0; final = -1; kadm_data_yes = 0; attr_types = NULL; if ((upd->kdb_update.kdbe_t_val = (kdbe_val_t *) malloc(MAXENTRY_SIZE)) == NULL) { return (ENOMEM); } /* * Find out which attrs have been modified */ if ((attr_types = (kdbe_attr_type_t *)malloc( sizeof (kdbe_attr_type_t) * MAXATTRS_SIZE)) == NULL) { return (ENOMEM); } if ((ret = krb5_db_get_principal_nolock(context, ent->princ, &curr, &nprincs, &more))) { free(attr_types); return (ret); } if (nprincs == 0) { /* * This is a new entry to the database, hence will * include all the attribute-value pairs * * We leave out the TL_DATA types which we model as * attrs in kdbe_attr_type_t, since listing AT_TL_DATA * encompasses these other types-turned-attributes * * So, we do *NOT* consider AT_MOD_PRINC, AT_MOD_TIME, * AT_MOD_WHERE, AT_PW_LAST_CHANGE, AT_PW_POLICY, * AT_PW_POLICY_SWITCH, AT_PW_HIST_KVNO and AT_PW_HIST, * totalling 8 attrs. */ while (nattrs < MAXATTRS_SIZE - 8) { attr_types[nattrs] = nattrs; nattrs++; } } else { find_changed_attrs(&curr, ent, attr_types, &nattrs); krb5_db_free_principal(context, &curr, nprincs); } for (i = 0; i < nattrs; i++) { switch (attr_types[i]) { case AT_ATTRFLAGS: if (ent->attributes >= 0) { ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_ATTRFLAGS; ULOG_ENTRY(upd, final).av_attrflags = (uint32_t)ent->attributes; } break; case AT_MAX_LIFE: if (ent->max_life >= 0) { ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_MAX_LIFE; ULOG_ENTRY(upd, final).av_max_life = (uint32_t)ent->max_life; } break; case AT_MAX_RENEW_LIFE: if (ent->max_renewable_life >= 0) { ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_MAX_RENEW_LIFE; ULOG_ENTRY(upd, final).av_max_renew_life = (uint32_t)ent->max_renewable_life; } break; case AT_EXP: if (ent->expiration >= 0) { ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_EXP; ULOG_ENTRY(upd, final).av_exp = (uint32_t)ent->expiration; } break; case AT_PW_EXP: if (ent->pw_expiration >= 0) { ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_PW_EXP; ULOG_ENTRY(upd, final).av_pw_exp = (uint32_t)ent->pw_expiration; } break; case AT_LAST_SUCCESS: if (ent->last_success >= 0) { ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_LAST_SUCCESS; ULOG_ENTRY(upd, final).av_last_success = (uint32_t)ent->last_success; } break; case AT_LAST_FAILED: if (ent->last_failed >= 0) { ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_LAST_FAILED; ULOG_ENTRY(upd, final).av_last_failed = (uint32_t)ent->last_failed; } break; case AT_FAIL_AUTH_COUNT: ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_FAIL_AUTH_COUNT; ULOG_ENTRY(upd, final).av_fail_auth_count = (uint32_t)ent->fail_auth_count; break; case AT_PRINC: if (ent->princ->length > 0) { ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_PRINC; if ((ret = conv_princ_2ulog(ent->princ, upd, final, REG_PRINC))) { free(attr_types); return (ret); } } break; case AT_KEYDATA: /* BEGIN CSTYLED */ if (ent->n_key_data >= 0) { ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_KEYDATA; ULOG_ENTRY(upd, final).av_keydata.av_keydata_len = ent->n_key_data; ULOG_ENTRY(upd, final).av_keydata.av_keydata_val = malloc(ent->n_key_data * sizeof (kdbe_key_t)); if (ULOG_ENTRY(upd, final).av_keydata.av_keydata_val == NULL) { free(attr_types); return (ENOMEM); } for (j = 0; j < ent->n_key_data; j++) { ULOG_ENTRY_KEYVAL(upd, final, j).k_ver = ent->key_data[j].key_data_ver; ULOG_ENTRY_KEYVAL(upd, final, j).k_kvno = ent->key_data[j].key_data_kvno; ULOG_ENTRY_KEYVAL(upd, final, j).k_enctype.k_enctype_len = ent->key_data[j].key_data_ver; ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_len = ent->key_data[j].key_data_ver; ULOG_ENTRY_KEYVAL(upd, final, j).k_enctype.k_enctype_val = malloc(ent->key_data[j].key_data_ver * sizeof(int32_t)); if (ULOG_ENTRY_KEYVAL(upd, final, j).k_enctype.k_enctype_val == NULL) { free(attr_types); return (ENOMEM); } ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_val = malloc(ent->key_data[j].key_data_ver * sizeof(utf8str_t)); if (ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_val == NULL) { free(attr_types); return (ENOMEM); } for (cnt = 0; cnt < ent->key_data[j].key_data_ver; cnt++) { ULOG_ENTRY_KEYVAL(upd, final, j).k_enctype.k_enctype_val[cnt] = ent->key_data[j].key_data_type[cnt]; ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_val[cnt].utf8str_t_len = ent->key_data[j].key_data_length[cnt]; ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_val[cnt].utf8str_t_val = malloc(ent->key_data[j].key_data_length[cnt] * sizeof (char)); if (ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_val[cnt].utf8str_t_val == NULL) { free(attr_types); return (ENOMEM); } (void) memcpy(ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_val[cnt].utf8str_t_val, ent->key_data[j].key_data_contents[cnt], ent->key_data[j].key_data_length[cnt]); } } } break; case AT_TL_DATA: ret = krb5_dbe_lookup_last_pwd_change(context, ent, &tmpint); if (ret == 0) { ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_PW_LAST_CHANGE; ULOG_ENTRY(upd, final).av_pw_last_change = tmpint; } tmpint = 0; if(!(ret = krb5_dbe_lookup_mod_princ_data( context, ent, &tmpint, &tmpprinc))) { ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_MOD_PRINC; ret = conv_princ_2ulog(tmpprinc, upd, final, MOD_PRINC); krb5_free_principal(context, tmpprinc); if (ret) { free(attr_types); return (ret); } ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_MOD_TIME; ULOG_ENTRY(upd, final).av_mod_time = tmpint; } newtl = ent->tl_data; while (newtl) { switch (newtl->tl_data_type) { case KRB5_TL_LAST_PWD_CHANGE: case KRB5_TL_MOD_PRINC: break; case KRB5_TL_KADM_DATA: default: if (kadm_data_yes == 0) { ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_TL_DATA; ULOG_ENTRY(upd, final).av_tldata.av_tldata_len = 0; ULOG_ENTRY(upd, final).av_tldata.av_tldata_val = malloc(ent->n_tl_data * sizeof(kdbe_tl_t)); if (ULOG_ENTRY(upd, final).av_tldata.av_tldata_val == NULL) { free(attr_types); return (ENOMEM); } kadm_data_yes = 1; } tmpint = ULOG_ENTRY(upd, final).av_tldata.av_tldata_len; ULOG_ENTRY(upd, final).av_tldata.av_tldata_len++; ULOG_ENTRY(upd, final).av_tldata.av_tldata_val[tmpint].tl_type = newtl->tl_data_type; ULOG_ENTRY(upd, final).av_tldata.av_tldata_val[tmpint].tl_data.tl_data_len = newtl->tl_data_length; ULOG_ENTRY(upd, final).av_tldata.av_tldata_val[tmpint].tl_data.tl_data_val = malloc(newtl->tl_data_length * sizeof (char)); if (ULOG_ENTRY(upd, final).av_tldata.av_tldata_val[tmpint].tl_data.tl_data_val == NULL) { free(attr_types); return (ENOMEM); } (void) memcpy(ULOG_ENTRY(upd, final).av_tldata.av_tldata_val[tmpint].tl_data.tl_data_val, newtl->tl_data_contents, newtl->tl_data_length); break; } newtl = newtl->tl_data_next; } break; /* END CSTYLED */ case AT_LEN: ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_LEN; ULOG_ENTRY(upd, final).av_len = (int16_t)ent->len; break; default: break; } } if (attr_types) free(attr_types); /* * Update len field in kdb_update */ upd->kdb_update.kdbe_t_len = ++final; /* * Bump up to next struct */ upd++; ent++; } return (0); } /* * This routine converts one or more update log (ulog) entries into * kerberos db2 records. Required memory should be allocated * for the db2 records (pointed to by krb5_db_entry *ent), prior * to calling this routine. */ krb5_error_code ulog_conv_2dbentry(krb5_context context, krb5_db_entry *entries, kdb_incr_update_t *updates, int nentries) { int k; krb5_db_entry *ent; kdb_incr_update_t *upd; if ((updates == NULL) || (entries == NULL)) return (KRB5KRB_ERR_GENERIC); ent = entries; upd = updates; for (k = 0; k < nentries; k++) { krb5_principal mod_princ = NULL; int i, j, cnt = 0, mod_time = 0, nattrs, nprincs = 0; krb5_principal dbprinc; char *dbprincstr = NULL; krb5_tl_data *newtl = NULL; krb5_error_code ret; unsigned int more; unsigned int prev_n_keys = 0; /* * If the ulog entry represents a DELETE update, * just skip to the next entry. */ if (upd->kdb_deleted == TRUE) goto next; /* * Store the no. of changed attributes in nattrs */ nattrs = upd->kdb_update.kdbe_t_len; dbprincstr = malloc((upd->kdb_princ_name.utf8str_t_len + 1) * sizeof (char)); if (dbprincstr == NULL) return (ENOMEM); strncpy(dbprincstr, (char *)upd->kdb_princ_name.utf8str_t_val, upd->kdb_princ_name.utf8str_t_len); dbprincstr[upd->kdb_princ_name.utf8str_t_len] = 0; ret = krb5_parse_name(context, dbprincstr, &dbprinc); free(dbprincstr); if (ret) return (ret); ret = krb5_db_get_principal(context, dbprinc, ent, &nprincs, &more); krb5_free_principal(context, dbprinc); if (ret) return (ret); /* * Set ent->n_tl_data = 0 initially, if this is an ADD update */ if (nprincs == 0) ent->n_tl_data = 0; for (i = 0; i < nattrs; i++) { krb5_principal tmpprinc = NULL; #define u (ULOG_ENTRY(upd, i)) switch (ULOG_ENTRY_TYPE(upd, i).av_type) { case AT_ATTRFLAGS: ent->attributes = (krb5_flags) u.av_attrflags; break; case AT_MAX_LIFE: ent->max_life = (krb5_deltat) u.av_max_life; break; case AT_MAX_RENEW_LIFE: ent->max_renewable_life = (krb5_deltat) u.av_max_renew_life; break; case AT_EXP: ent->expiration = (krb5_timestamp) u.av_exp; break; case AT_PW_EXP: ent->pw_expiration = (krb5_timestamp) u.av_pw_exp; break; case AT_LAST_SUCCESS: ent->last_success = (krb5_timestamp) u.av_last_success; break; case AT_LAST_FAILED: ent->last_failed = (krb5_timestamp) u.av_last_failed; break; case AT_FAIL_AUTH_COUNT: ent->fail_auth_count = (krb5_kvno) u.av_fail_auth_count; break; case AT_PRINC: tmpprinc = conv_princ_2db(context, &u.av_princ); if (tmpprinc == NULL) return ENOMEM; if (nprincs) krb5_free_principal(context, ent->princ); ent->princ = tmpprinc; break; case AT_KEYDATA: if (nprincs != 0) prev_n_keys = ent->n_key_data; else prev_n_keys = 0; ent->n_key_data = (krb5_int16)u.av_keydata.av_keydata_len; if (nprincs == 0) ent->key_data = NULL; ent->key_data = (krb5_key_data *)realloc(ent->key_data, (ent->n_key_data * sizeof (krb5_key_data))); /* XXX Memory leak: Old key data in records eliminated by resizing to smaller size. */ if (ent->key_data == NULL) /* XXX Memory leak: old storage. */ return (ENOMEM); /* BEGIN CSTYLED */ for (j = prev_n_keys; j < ent->n_key_data; j++) { for (cnt = 0; cnt < 2; cnt++) { ent->key_data[j].key_data_contents[cnt] = NULL; } } for (j = 0; j < ent->n_key_data; j++) { krb5_key_data *kp = &ent->key_data[j]; kdbe_key_t *kv = &ULOG_ENTRY_KEYVAL(upd, i, j); kp->key_data_ver = (krb5_int16)kv->k_ver; kp->key_data_kvno = (krb5_int16)kv->k_kvno; if (kp->key_data_ver > 2) { return EINVAL; /* XXX ? */ } for (cnt = 0; cnt < kp->key_data_ver; cnt++) { void *newptr; kp->key_data_type[cnt] = (krb5_int16)kv->k_enctype.k_enctype_val[cnt]; kp->key_data_length[cnt] = (krb5_int16)kv->k_contents.k_contents_val[cnt].utf8str_t_len; newptr = realloc(kp->key_data_contents[cnt], kp->key_data_length[cnt]); if (newptr == NULL) return ENOMEM; kp->key_data_contents[cnt] = newptr; (void) memset(kp->key_data_contents[cnt], 0, kp->key_data_length[cnt]); (void) memcpy(kp->key_data_contents[cnt], kv->k_contents.k_contents_val[cnt].utf8str_t_val, kp->key_data_length[cnt]); } } break; case AT_TL_DATA: cnt = u.av_tldata.av_tldata_len; newtl = malloc(cnt * sizeof (krb5_tl_data)); (void) memset(newtl, 0, (cnt * sizeof (krb5_tl_data))); if (newtl == NULL) return (ENOMEM); for (j = 0; j < cnt; j++) { newtl[j].tl_data_type = (krb5_int16)u.av_tldata.av_tldata_val[j].tl_type; newtl[j].tl_data_length = (krb5_int16)u.av_tldata.av_tldata_val[j].tl_data.tl_data_len; newtl[j].tl_data_contents = NULL; newtl[j].tl_data_contents = malloc(newtl[j].tl_data_length * sizeof (krb5_octet)); if (newtl[j].tl_data_contents == NULL) /* XXX Memory leak: newtl and previously allocated elements. */ return (ENOMEM); (void) memset(newtl[j].tl_data_contents, 0, (newtl[j].tl_data_length * sizeof (krb5_octet))); (void) memcpy(newtl[j].tl_data_contents, u.av_tldata.av_tldata_val[j].tl_data.tl_data_val, newtl[j].tl_data_length); newtl[j].tl_data_next = NULL; if (j > 0) newtl[j - 1].tl_data_next = &newtl[j]; } if ((ret = krb5_dbe_update_tl_data(context, ent, newtl))) return (ret); for (j = 0; j < cnt; j++) if (newtl[j].tl_data_contents) { free(newtl[j].tl_data_contents); newtl[j].tl_data_contents = NULL; } if (newtl) { free(newtl); newtl = NULL; } break; /* END CSTYLED */ case AT_PW_LAST_CHANGE: if ((ret = krb5_dbe_update_last_pwd_change(context, ent, u.av_pw_last_change))) return (ret); break; case AT_MOD_PRINC: tmpprinc = conv_princ_2db(context, &u.av_mod_princ); if (tmpprinc == NULL) return ENOMEM; mod_princ = tmpprinc; break; case AT_MOD_TIME: mod_time = u.av_mod_time; break; case AT_LEN: ent->len = (krb5_int16) u.av_len; break; default: break; } #undef u } /* * process mod_princ_data request */ if (mod_time && mod_princ) { ret = krb5_dbe_update_mod_princ_data(context, ent, mod_time, mod_princ); krb5_free_principal(context, mod_princ); mod_princ = NULL; if (ret) return (ret); } next: /* * Bump up to next struct */ upd++; ent++; } return (0); } /* * This routine frees up memory associated with the bunched ulog entries. */ void ulog_free_entries(kdb_incr_update_t *updates, int no_of_updates) { kdb_incr_update_t *upd; int i, j, k, cnt; if (updates == NULL) return; upd = updates; /* * Loop thru each ulog entry */ for (cnt = 0; cnt < no_of_updates; cnt++) { /* * ulog entry - kdb_princ_name */ free(upd->kdb_princ_name.utf8str_t_val); /* BEGIN CSTYLED */ /* * ulog entry - kdb_kdcs_seen_by */ if (upd->kdb_kdcs_seen_by.kdb_kdcs_seen_by_val) { for (i = 0; i < upd->kdb_kdcs_seen_by.kdb_kdcs_seen_by_len; i++) free(upd->kdb_kdcs_seen_by.kdb_kdcs_seen_by_val[i].utf8str_t_val); free(upd->kdb_kdcs_seen_by.kdb_kdcs_seen_by_val); } /* * ulog entry - kdb_futures */ free(upd->kdb_futures.kdb_futures_val); /* * ulog entry - kdb_update */ if (upd->kdb_update.kdbe_t_val) { /* * Loop thru all the attributes and free up stuff */ for (i = 0; i < upd->kdb_update.kdbe_t_len; i++) { /* * Free av_key_data */ if ((ULOG_ENTRY_TYPE(upd, i).av_type == AT_KEYDATA) && ULOG_ENTRY(upd, i).av_keydata.av_keydata_val) { for (j = 0; j < ULOG_ENTRY(upd, i).av_keydata.av_keydata_len; j++) { free(ULOG_ENTRY_KEYVAL(upd, i, j).k_enctype.k_enctype_val); if (ULOG_ENTRY_KEYVAL(upd, i, j).k_contents.k_contents_val) { for (k = 0; k < ULOG_ENTRY_KEYVAL(upd, i, j).k_ver; k++) { free(ULOG_ENTRY_KEYVAL(upd, i, j).k_contents.k_contents_val[k].utf8str_t_val); } free(ULOG_ENTRY_KEYVAL(upd, i, j).k_contents.k_contents_val); } } free(ULOG_ENTRY(upd, i).av_keydata.av_keydata_val); } /* * Free av_tl_data */ if ((ULOG_ENTRY_TYPE(upd, i).av_type == AT_TL_DATA) && ULOG_ENTRY(upd, i).av_tldata.av_tldata_val) { for (j = 0; j < ULOG_ENTRY(upd, i).av_tldata.av_tldata_len; j++) { free(ULOG_ENTRY(upd, i).av_tldata.av_tldata_val[j].tl_data.tl_data_val); } free(ULOG_ENTRY(upd, i).av_tldata.av_tldata_val); } /* * Free av_princ */ if (ULOG_ENTRY_TYPE(upd, i).av_type == AT_PRINC) { free(ULOG_ENTRY(upd, i).av_princ.k_realm.utf8str_t_val); if (ULOG_ENTRY(upd, i).av_princ.k_components.k_components_val) { for (j = 0; j < ULOG_ENTRY(upd, i).av_princ.k_components.k_components_len; j++) { free(ULOG_ENTRY_PRINC(upd, i, j).k_data.utf8str_t_val); } free(ULOG_ENTRY(upd, i).av_princ.k_components.k_components_val); } } /* * Free av_mod_princ */ if (ULOG_ENTRY_TYPE(upd, i).av_type == AT_MOD_PRINC) { free(ULOG_ENTRY(upd, i).av_mod_princ.k_realm.utf8str_t_val); if (ULOG_ENTRY(upd, i).av_mod_princ.k_components.k_components_val) { for (j = 0; j < ULOG_ENTRY(upd, i).av_mod_princ.k_components.k_components_len; j++) { free(ULOG_ENTRY_MOD_PRINC(upd, i, j).k_data.utf8str_t_val); } free(ULOG_ENTRY(upd, i).av_mod_princ.k_components.k_components_val); } } /* * Free av_mod_where */ if ((ULOG_ENTRY_TYPE(upd, i).av_type == AT_MOD_WHERE) && ULOG_ENTRY(upd, i).av_mod_where.utf8str_t_val) free(ULOG_ENTRY(upd, i).av_mod_where.utf8str_t_val); /* * Free av_pw_policy */ if ((ULOG_ENTRY_TYPE(upd, i).av_type == AT_PW_POLICY) && ULOG_ENTRY(upd, i).av_pw_policy.utf8str_t_val) free(ULOG_ENTRY(upd, i).av_pw_policy.utf8str_t_val); /* * XXX: Free av_pw_hist * * For now, we just free the pointer * to av_pw_hist_val, since we aren't * populating this union member in * the conv api function(s) anyways. */ if ((ULOG_ENTRY_TYPE(upd, i).av_type == AT_PW_HIST) && ULOG_ENTRY(upd, i).av_pw_hist.av_pw_hist_val) free(ULOG_ENTRY(upd, i).av_pw_hist.av_pw_hist_val); } /* * Free up the pointer to kdbe_t_val */ free(upd->kdb_update.kdbe_t_val); } /* END CSTYLED */ /* * Bump up to next struct */ upd++; } /* * Finally, free up the pointer to the bunched ulog entries */ free(updates); } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * lib/kdb/kdb_cpw.c * * Copyright 1995 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ /* * Copyright (C) 1998 by the FundsXpress, INC. * * All rights reserved. * * Export of this software from the United States of America may require * a specific license from the United States Government. It is the * responsibility of any person or organization contemplating export to * obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. FundsXpress makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "k5-int.h" #include "kdb.h" #include #include static int get_key_data_kvno(context, count, data) krb5_context context; int count; krb5_key_data * data; { int i, kvno; /* Find last key version number */ for (kvno = i = 0; i < count; i++) { if (kvno < data[i].key_data_kvno) { kvno = data[i].key_data_kvno; } } return(kvno); } static void cleanup_key_data(context, count, data) krb5_context context; int count; krb5_key_data * data; { int i, j; /* If data is NULL, count is always 0 */ if (data == NULL) return; for (i = 0; i < count; i++) { for (j = 0; j < data[i].key_data_ver; j++) { if (data[i].key_data_length[j]) { krb5_db_free(context, data[i].key_data_contents[j]); } } } krb5_db_free(context, data); } static krb5_error_code add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno) krb5_context context; krb5_keyblock * master_key; krb5_key_salt_tuple * ks_tuple; int ks_tuple_count; krb5_db_entry * db_entry; int kvno; { krb5_principal krbtgt_princ; krb5_keyblock key; krb5_db_entry krbtgt_entry; krb5_boolean more; int max_kvno, one, i, j, k; krb5_error_code retval; krb5_key_data tmp_key_data; krb5_key_data *tptr; memset( &tmp_key_data, 0, sizeof(tmp_key_data)); retval = krb5_build_principal_ext(context, &krbtgt_princ, db_entry->princ->realm.length, db_entry->princ->realm.data, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME, db_entry->princ->realm.length, db_entry->princ->realm.data, 0); if (retval) return retval; /* Get tgt from database */ retval = krb5_db_get_principal(context, krbtgt_princ, &krbtgt_entry, &one, &more); krb5_free_principal(context, krbtgt_princ); /* don't need it anymore */ if (retval) return(retval); if ((one > 1) || (more)) { krb5_db_free_principal(context, &krbtgt_entry, one); return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; } if (!one) return KRB5_KDB_NOENTRY; /* Get max kvno */ for (max_kvno = j = 0; j < krbtgt_entry.n_key_data; j++) { if (max_kvno < krbtgt_entry.key_data[j].key_data_kvno) { max_kvno = krbtgt_entry.key_data[j].key_data_kvno; } } for (i = 0; i < ks_tuple_count; i++) { krb5_boolean similar; similar = 0; /* * We could use krb5_keysalt_iterate to replace this loop, or use * krb5_keysalt_is_present for the loop below, but we want to avoid * circular library dependencies. */ for (j = 0; j < i; j++) { if ((retval = krb5_c_enctype_compare(context, ks_tuple[i].ks_enctype, ks_tuple[j].ks_enctype, &similar))) return(retval); if (similar) break; } if (similar) continue; if ((retval = krb5_dbe_create_key_data(context, db_entry))) goto add_key_rnd_err; /* there used to be code here to extract the old key, and derive a new key from it. Now that there's a unified prng, that isn't necessary. */ /* make new key */ if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype, &key))) goto add_key_rnd_err; /* db library will free this. Since, its a so, it could actually be using different memory management function. So, its better if the memory is allocated by the db's malloc. So, a temporary memory is used here which will later be copied to the db_entry */ retval = krb5_dbekd_encrypt_key_data(context, master_key, &key, NULL, kvno, &tmp_key_data); krb5_free_keyblock_contents(context, &key); if( retval ) goto add_key_rnd_err; tptr = &db_entry->key_data[db_entry->n_key_data-1]; tptr->key_data_ver = tmp_key_data.key_data_ver; tptr->key_data_kvno = tmp_key_data.key_data_kvno; for( k = 0; k < tmp_key_data.key_data_ver; k++ ) { tptr->key_data_type[k] = tmp_key_data.key_data_type[k]; tptr->key_data_length[k] = tmp_key_data.key_data_length[k]; if( tmp_key_data.key_data_contents[k] ) { tptr->key_data_contents[k] = krb5_db_alloc(context, NULL, tmp_key_data.key_data_length[k]); if( tptr->key_data_contents[k] == NULL ) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); db_entry->key_data = NULL; db_entry->n_key_data = 0; retval = ENOMEM; goto add_key_rnd_err; } memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]); memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]); free( tmp_key_data.key_data_contents[k] ); tmp_key_data.key_data_contents[k] = NULL; } } } add_key_rnd_err: krb5_db_free_principal(context, &krbtgt_entry, one); for( i = 0; i < tmp_key_data.key_data_ver; i++ ) { if( tmp_key_data.key_data_contents[i] ) { memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]); free( tmp_key_data.key_data_contents[i] ); } } return(retval); } /* * Change random key for a krb5_db_entry * Assumes the max kvno * * As a side effect all old keys are nuked if keepold is false. */ krb5_error_code krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry) krb5_context context; krb5_keyblock * master_key; krb5_key_salt_tuple * ks_tuple; int ks_tuple_count; krb5_boolean keepold; krb5_db_entry * db_entry; { int key_data_count; int n_new_key_data; krb5_key_data * key_data; krb5_error_code retval; int kvno; int i; /* First save the old keydata */ kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data); key_data_count = db_entry->n_key_data; key_data = db_entry->key_data; db_entry->key_data = NULL; db_entry->n_key_data = 0; /* increment the kvno */ kvno++; retval = add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno); if (retval) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); db_entry->n_key_data = key_data_count; db_entry->key_data = key_data; } else if (keepold) { n_new_key_data = db_entry->n_key_data; for (i = 0; i < key_data_count; i++) { retval = krb5_dbe_create_key_data(context, db_entry); if (retval) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); break; } db_entry->key_data[i+n_new_key_data] = key_data[i]; memset(&key_data[i], 0, sizeof(krb5_key_data)); } krb5_db_free(context, key_data); /* we moved the cotents to new memory. But, the original block which contained the data */ } else { cleanup_key_data(context, key_data_count, key_data); } return(retval); } /* * Add random key for a krb5_db_entry * Assumes the max kvno * * As a side effect all old keys older than the max kvno are nuked. */ krb5_error_code krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry) krb5_context context; krb5_keyblock * master_key; krb5_key_salt_tuple * ks_tuple; int ks_tuple_count; krb5_db_entry * db_entry; { int key_data_count; krb5_key_data * key_data; krb5_error_code retval; int kvno; int i; /* First save the old keydata */ kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data); key_data_count = db_entry->n_key_data; key_data = db_entry->key_data; db_entry->key_data = NULL; db_entry->n_key_data = 0; /* increment the kvno */ kvno++; if ((retval = add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno))) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); db_entry->n_key_data = key_data_count; db_entry->key_data = key_data; } else { /* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */ for (i = 0; i < key_data_count; i++) { if (key_data[i].key_data_kvno == (kvno - 1)) { if ((retval = krb5_dbe_create_key_data(context, db_entry))) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); break; } /* We should decrypt/re-encrypt the data to use the same mkvno*/ db_entry->key_data[db_entry->n_key_data - 1] = key_data[i]; memset(&key_data[i], 0, sizeof(krb5_key_data)); } } cleanup_key_data(context, key_data_count, key_data); } return(retval); } /* * Add key_data for a krb5_db_entry * If passwd is NULL the assumes that the caller wants a random password. */ static krb5_error_code add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry, kvno) krb5_context context; krb5_keyblock * master_key; krb5_key_salt_tuple * ks_tuple; int ks_tuple_count; char * passwd; krb5_db_entry * db_entry; int kvno; { krb5_error_code retval; krb5_keysalt key_salt; krb5_keyblock key; krb5_data pwd; int i, j, k; krb5_key_data tmp_key_data; krb5_key_data *tptr; memset( &tmp_key_data, 0, sizeof(tmp_key_data)); retval = 0; for (i = 0; i < ks_tuple_count; i++) { krb5_boolean similar; similar = 0; /* * We could use krb5_keysalt_iterate to replace this loop, or use * krb5_keysalt_is_present for the loop below, but we want to avoid * circular library dependencies. */ for (j = 0; j < i; j++) { if ((retval = krb5_c_enctype_compare(context, ks_tuple[i].ks_enctype, ks_tuple[j].ks_enctype, &similar))) return(retval); if (similar && (ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype)) break; } if (j < i) continue; if ((retval = krb5_dbe_create_key_data(context, db_entry))) return(retval); /* Convert password string to key using appropriate salt */ switch (key_salt.type = ks_tuple[i].ks_salttype) { case KRB5_KDB_SALTTYPE_ONLYREALM: { krb5_data * saltdata; if ((retval = krb5_copy_data(context, krb5_princ_realm(context, db_entry->princ), &saltdata))) return(retval); key_salt.data = *saltdata; krb5_xfree(saltdata); } break; case KRB5_KDB_SALTTYPE_NOREALM: if ((retval=krb5_principal2salt_norealm(context, db_entry->princ, &key_salt.data))) return(retval); break; case KRB5_KDB_SALTTYPE_NORMAL: if ((retval = krb5_principal2salt(context, db_entry->princ, &key_salt.data))) return(retval); break; case KRB5_KDB_SALTTYPE_V4: key_salt.data.length = 0; key_salt.data.data = 0; break; case KRB5_KDB_SALTTYPE_AFS3: { #if 0 krb5_data * saltdata; if (retval = krb5_copy_data(context, krb5_princ_realm(context, db_entry->princ), &saltdata)) return(retval); key_salt.data = *saltdata; key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/ krb5_xfree(saltdata); #else /* Why do we do this? Well, the afs_mit_string_to_key needs to use strlen, and the realm is not NULL terminated.... */ unsigned int slen = (*krb5_princ_realm(context,db_entry->princ)).length; if(!(key_salt.data.data = (char *) malloc(slen+1))) return ENOMEM; key_salt.data.data[slen] = 0; memcpy((char *)key_salt.data.data, (char *)(*krb5_princ_realm(context,db_entry->princ)).data, slen); key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/ #endif } break; default: return(KRB5_KDB_BAD_SALTTYPE); } pwd.data = passwd; pwd.length = strlen(passwd); /* Solaris Kerberos */ memset(&key, 0, sizeof (krb5_keyblock)); /* AFS string to key will happen here */ if ((retval = krb5_c_string_to_key(context, ks_tuple[i].ks_enctype, &pwd, &key_salt.data, &key))) { if (key_salt.data.data) free(key_salt.data.data); return(retval); } if (key_salt.data.length == SALT_TYPE_AFS_LENGTH) key_salt.data.length = krb5_princ_realm(context, db_entry->princ)->length; /* memory allocation to be done by db. So, use temporary block and later copy it to the memory allocated by db */ retval = krb5_dbekd_encrypt_key_data(context, master_key, &key, (const krb5_keysalt *)&key_salt, kvno, &tmp_key_data); if (key_salt.data.data) free(key_salt.data.data); /* Solaris Kerberos */ krb5_free_keyblock_contents(context, &key); if( retval ) return retval; tptr = &db_entry->key_data[db_entry->n_key_data-1]; tptr->key_data_ver = tmp_key_data.key_data_ver; tptr->key_data_kvno = tmp_key_data.key_data_kvno; for( k = 0; k < tmp_key_data.key_data_ver; k++ ) { tptr->key_data_type[k] = tmp_key_data.key_data_type[k]; tptr->key_data_length[k] = tmp_key_data.key_data_length[k]; if( tmp_key_data.key_data_contents[k] ) { tptr->key_data_contents[k] = krb5_db_alloc(context, NULL, tmp_key_data.key_data_length[k]); if( tptr->key_data_contents[k] == NULL ) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); db_entry->key_data = NULL; db_entry->n_key_data = 0; retval = ENOMEM; goto add_key_pwd_err; } memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]); memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]); free( tmp_key_data.key_data_contents[k] ); tmp_key_data.key_data_contents[k] = NULL; } } } add_key_pwd_err: for( i = 0; i < tmp_key_data.key_data_ver; i++ ) { if( tmp_key_data.key_data_contents[i] ) { memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]); free( tmp_key_data.key_data_contents[i] ); } } return(retval); } /* * Change password for a krb5_db_entry * Assumes the max kvno * * As a side effect all old keys are nuked if keepold is false. */ krb5_error_code krb5_dbe_def_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd, new_kvno, keepold, db_entry) krb5_context context; krb5_keyblock * master_key; krb5_key_salt_tuple * ks_tuple; int ks_tuple_count; char * passwd; int new_kvno; krb5_boolean keepold; krb5_db_entry * db_entry; { int key_data_count; int n_new_key_data; krb5_key_data * key_data; krb5_error_code retval; int old_kvno; int i; /* First save the old keydata */ old_kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data); key_data_count = db_entry->n_key_data; key_data = db_entry->key_data; db_entry->key_data = NULL; db_entry->n_key_data = 0; /* increment the kvno. if the requested kvno is too small, increment the old kvno */ if (new_kvno < old_kvno+1) new_kvno = old_kvno+1; retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry, new_kvno); if (retval) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); db_entry->n_key_data = key_data_count; db_entry->key_data = key_data; } else if (keepold) { n_new_key_data = db_entry->n_key_data; for (i = 0; i < key_data_count; i++) { retval = krb5_dbe_create_key_data(context, db_entry); if (retval) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); break; } db_entry->key_data[i+n_new_key_data] = key_data[i]; memset(&key_data[i], 0, sizeof(krb5_key_data)); } krb5_db_free( context, key_data ); } else { cleanup_key_data(context, key_data_count, key_data); } return(retval); } /* * Add password for a krb5_db_entry * Assumes the max kvno * * As a side effect all old keys older than the max kvno are nuked. */ krb5_error_code krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry) krb5_context context; krb5_keyblock * master_key; krb5_key_salt_tuple * ks_tuple; int ks_tuple_count; char * passwd; krb5_db_entry * db_entry; { int key_data_count; krb5_key_data * key_data; krb5_error_code retval; int old_kvno, new_kvno; int i; /* First save the old keydata */ old_kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data); key_data_count = db_entry->n_key_data; key_data = db_entry->key_data; db_entry->key_data = NULL; db_entry->n_key_data = 0; /* increment the kvno */ new_kvno = old_kvno+1; if ((retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry, new_kvno))) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); db_entry->n_key_data = key_data_count; db_entry->key_data = key_data; } else { /* Copy keys with key_data_kvno == old_kvno */ for (i = 0; i < key_data_count; i++) { if (key_data[i].key_data_kvno == old_kvno) { if ((retval = krb5_dbe_create_key_data(context, db_entry))) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); break; } /* We should decrypt/re-encrypt the data to use the same mkvno*/ db_entry->key_data[db_entry->n_key_data - 1] = key_data[i]; memset(&key_data[i], 0, sizeof(krb5_key_data)); } } cleanup_key_data(context, key_data_count, key_data); } return(retval); } /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * lib/kdb/kdb_helper.c * * Copyright 1995 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ #include "k5-int.h" #include "kdb.h" #include #include #include #include /* * Given a particular enctype and optional salttype and kvno, find the * most appropriate krb5_key_data entry of the database entry. * * If stype or kvno is negative, it is ignored. * If kvno is 0 get the key which is maxkvno for the princ and matches * the other attributes. */ krb5_error_code krb5_dbe_def_search_enctype(kcontext, dbentp, start, ktype, stype, kvno, kdatap) krb5_context kcontext; krb5_db_entry *dbentp; krb5_int32 *start; krb5_int32 ktype; krb5_int32 stype; krb5_int32 kvno; krb5_key_data **kdatap; { int i, idx; int maxkvno; krb5_key_data *datap; krb5_error_code ret; ret = 0; if (kvno == -1 && stype == -1 && ktype == -1) kvno = 0; if (kvno == 0) { /* Get the max key version */ for (i = 0; i < dbentp->n_key_data; i++) { if (kvno < dbentp->key_data[i].key_data_kvno) { kvno = dbentp->key_data[i].key_data_kvno; } } } maxkvno = -1; datap = (krb5_key_data *) NULL; for (i = *start; i < dbentp->n_key_data; i++) { krb5_boolean similar; krb5_int32 db_stype; ret = 0; if (dbentp->key_data[i].key_data_ver > 1) { db_stype = dbentp->key_data[i].key_data_type[1]; } else { db_stype = KRB5_KDB_SALTTYPE_NORMAL; } /* * Filter out non-permitted enctypes. */ if (!krb5_is_permitted_enctype(kcontext, dbentp->key_data[i].key_data_type[0])) { ret = KRB5_KDB_NO_PERMITTED_KEY; continue; } if (ktype > 0) { if ((ret = krb5_c_enctype_compare(kcontext, (krb5_enctype) ktype, dbentp->key_data[i].key_data_type[0], &similar))) return(ret); } if (((ktype <= 0) || similar) && ((db_stype == stype) || (stype < 0))) { if (kvno >= 0) { if (kvno == dbentp->key_data[i].key_data_kvno) { datap = &dbentp->key_data[i]; idx = i; maxkvno = kvno; break; } } else { if (dbentp->key_data[i].key_data_kvno > maxkvno) { maxkvno = dbentp->key_data[i].key_data_kvno; datap = &dbentp->key_data[i]; idx = i; } } } } if (maxkvno < 0) return ret ? ret : KRB5_KDB_NO_MATCHING_KEY; *kdatap = datap; *start = idx+1; return 0; } /* * kdb default functions. Ideally, some other file should have this functions. For now, TBD. */ #ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif krb5_error_code krb5_def_store_mkey(context, keyfile, mname, key, master_pwd) krb5_context context; char *keyfile; krb5_principal mname; krb5_keyblock *key; char *master_pwd; { FILE *kf; krb5_error_code retval = 0; krb5_ui_2 enctype; char defkeyfile[MAXPATHLEN+1]; krb5_data *realm = krb5_princ_realm(context, mname); #if HAVE_UMASK mode_t oumask; #endif if (!keyfile) { (void) strcpy(defkeyfile, DEFAULT_KEYFILE_STUB); (void) strncat(defkeyfile, realm->data, min(sizeof(defkeyfile)-sizeof(DEFAULT_KEYFILE_STUB)-1, realm->length)); defkeyfile[sizeof(defkeyfile) - 1] = '\0'; keyfile = defkeyfile; } #if HAVE_UMASK oumask = umask(077); #endif #ifdef ANSI_STDIO /* Solaris Kerberos: using F to deal with 256 open file limit */ if (!(kf = fopen(keyfile, "wbF"))) #else if (!(kf = fopen(keyfile, "wF"))) #endif { int e = errno; #if HAVE_UMASK (void) umask(oumask); #endif krb5_set_error_message (context, e, gettext("%s accessing file '%s'"), error_message (e), keyfile); return e; } enctype = key->enctype; if ((fwrite((krb5_pointer) &enctype, 2, 1, kf) != 1) || (fwrite((krb5_pointer) &key->length, sizeof(key->length), 1, kf) != 1) || (fwrite((krb5_pointer) key->contents, sizeof(key->contents[0]), (unsigned) key->length, kf) != key->length)) { retval = errno; (void) fclose(kf); } if (fclose(kf) == EOF) retval = errno; #if HAVE_UMASK (void) umask(oumask); #endif return retval; } krb5_error_code krb5_db_def_fetch_mkey( krb5_context context, krb5_principal mname, krb5_keyblock *key, int *kvno, char *db_args) { krb5_error_code retval; krb5_ui_2 enctype; char defkeyfile[MAXPATHLEN+1]; krb5_data *realm = krb5_princ_realm(context, mname); FILE *kf = NULL; retval = 0; key->magic = KV5M_KEYBLOCK; (void) strcpy(defkeyfile, DEFAULT_KEYFILE_STUB); (void) strncat(defkeyfile, realm->data, min(sizeof(defkeyfile)-sizeof(DEFAULT_KEYFILE_STUB)-1, realm->length)); defkeyfile[sizeof(defkeyfile) - 1] = '\0'; #ifdef ANSI_STDIO /* Solaris Kerberos: using F to deal with 256 open file limit */ if (!(kf = fopen((db_args) ? db_args : defkeyfile, "rbF"))) #else if (!(kf = fopen((db_args) ? db_args : defkeyfile, "rF"))) #endif return KRB5_KDB_CANTREAD_STORED; if (fread((krb5_pointer) &enctype, 2, 1, kf) != 1) { retval = KRB5_KDB_CANTREAD_STORED; goto errout; } if (key->enctype == ENCTYPE_UNKNOWN) key->enctype = enctype; else if (enctype != key->enctype) { retval = KRB5_KDB_BADSTORED_MKEY; goto errout; } if (fread((krb5_pointer) &key->length, sizeof(key->length), 1, kf) != 1) { retval = KRB5_KDB_CANTREAD_STORED; goto errout; } if (!key->length || ((int) key->length) < 0) { retval = KRB5_KDB_BADSTORED_MKEY; goto errout; } if (!(key->contents = (krb5_octet *)malloc(key->length))) { retval = ENOMEM; goto errout; } if (fread((krb5_pointer) key->contents, sizeof(key->contents[0]), key->length, kf) != key->length) { retval = KRB5_KDB_CANTREAD_STORED; memset(key->contents, 0, key->length); free(key->contents); key->contents = 0; } else retval = 0; *kvno = 0; errout: (void) fclose(kf); return retval; } krb5_error_code krb5_def_verify_master_key(context, mprinc, mkey) krb5_context context; krb5_principal mprinc; krb5_keyblock *mkey; { krb5_error_code retval; krb5_db_entry master_entry; int nprinc; krb5_boolean more; krb5_keyblock tempkey; nprinc = 1; if ((retval = krb5_db_get_principal(context, mprinc, &master_entry, &nprinc, &more))) return(retval); if (nprinc != 1) { if (nprinc) krb5_db_free_principal(context, &master_entry, nprinc); return(KRB5_KDB_NOMASTERKEY); } else if (more) { krb5_db_free_principal(context, &master_entry, nprinc); return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE); } if ((retval = krb5_dbekd_decrypt_key_data(context, mkey, &master_entry.key_data[0], &tempkey, NULL))) { krb5_db_free_principal(context, &master_entry, nprinc); return retval; } if (mkey->length != tempkey.length || memcmp((char *)mkey->contents, (char *)tempkey.contents,mkey->length)) { retval = KRB5_KDB_BADMASTERKEY; } memset((char *)tempkey.contents, 0, tempkey.length); krb5_xfree(tempkey.contents); krb5_db_free_principal(context, &master_entry, nprinc); return retval; } krb5_error_code kdb_def_set_mkey ( krb5_context kcontext, char *pwd, krb5_keyblock *key ) { /* printf("default set master key\n"); */ return 0; } krb5_error_code kdb_def_get_mkey ( krb5_context kcontext, krb5_keyblock **key ) { /* printf("default get master key\n"); */ return 0; } krb5_error_code krb5_def_promote_db (krb5_context kcontext, char *s, char **args) { /* printf("default promote_db\n"); */ return KRB5_PLUGIN_OP_NOTSUPP; } /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include "kdb_log.h" /* * This modules includes all the necessary functions that create and * modify the Kerberos principal update and header logs. */ #define getpagesize() sysconf(_SC_PAGESIZE) static int pagesize = 0; #define INIT_ULOG(ctx) log_ctx = ctx->kdblog_context; \ ulog = log_ctx->ulog /* * Sync update entry to disk. */ krb5_error_code ulog_sync_update(kdb_hlog_t *ulog, kdb_ent_header_t *upd) { ulong_t start, end, size; krb5_error_code retval; if (ulog == NULL) return (KRB5_LOG_ERROR); if (!pagesize) pagesize = getpagesize(); start = ((ulong_t)upd) & (~(pagesize-1)); end = (((ulong_t)upd) + ulog->kdb_block + (pagesize-1)) & (~(pagesize-1)); size = end - start; if (retval = msync((caddr_t)start, size, MS_SYNC)) { return (retval); } return (0); } /* * Sync memory to disk for the update log header. */ void ulog_sync_header(kdb_hlog_t *ulog) { if (!pagesize) pagesize = getpagesize(); if (msync((caddr_t)ulog, pagesize, MS_SYNC)) { /* * Couldn't sync to disk, let's panic */ syslog(LOG_ERR, "ulog_sync_header: could not sync to disk"); abort(); } } /* * Resizes the array elements. We reinitialize the update log rather than * unrolling the the log and copying it over to a temporary log for obvious * performance reasons. Slaves will subsequently do a full resync, but * the need for resizing should be very small. */ krb5_error_code ulog_resize(kdb_hlog_t *ulog, uint32_t ulogentries, int ulogfd, uint_t recsize) { uint_t new_block, new_size; if (ulog == NULL) return (KRB5_LOG_ERROR); new_size = sizeof (kdb_hlog_t); new_block = (recsize / ULOG_BLOCK) + 1; new_block *= ULOG_BLOCK; new_size += ulogentries * new_block; if (new_size <= MAXLOGLEN) { /* * Reinit log with new block size */ (void) memset(ulog, 0, sizeof (kdb_hlog_t)); ulog->kdb_hmagic = KDB_HMAGIC; ulog->db_version_num = KDB_VERSION; ulog->kdb_state = KDB_STABLE; ulog->kdb_block = new_block; ulog_sync_header(ulog); /* * Time to expand log considering new block size */ if (lseek(ulogfd, new_size, SEEK_SET) == -1) { return (errno); } if (write(ulogfd, "+", 1) != 1) { return (errno); } } else { /* * Can't map into file larger than MAXLOGLEN */ return (KRB5_LOG_ERROR); } return (0); } /* * Adds an entry to the update log. * The layout of the update log looks like: * * header log -> [ update header -> xdr(kdb_incr_update_t) ], ... */ krb5_error_code ulog_add_update(krb5_context context, kdb_incr_update_t *upd) { XDR xdrs; kdbe_time_t ktime; struct timeval timestamp; kdb_ent_header_t *indx_log; uint_t i, recsize; ulong_t upd_size; krb5_error_code retval; kdb_sno_t cur_sno; kdb_log_context *log_ctx; kdb_hlog_t *ulog = NULL; uint32_t ulogentries; int ulogfd; INIT_ULOG(context); ulogentries = log_ctx->ulogentries; ulogfd = log_ctx->ulogfd; if (upd == NULL) return (KRB5_LOG_ERROR); (void) gettimeofday(×tamp, NULL); ktime.seconds = timestamp.tv_sec; ktime.useconds = timestamp.tv_usec; upd_size = xdr_sizeof((xdrproc_t)xdr_kdb_incr_update_t, upd); recsize = sizeof (kdb_ent_header_t) + upd_size; if (recsize > ulog->kdb_block) { if (retval = ulog_resize(ulog, ulogentries, ulogfd, recsize)) { /* Resize element array failed */ return (retval); } } cur_sno = ulog->kdb_last_sno; /* * We need to overflow our sno, replicas will do full * resyncs once they see their sno > than the masters. */ if (cur_sno == ULONG_MAX) cur_sno = 1; else cur_sno++; /* * We squirrel this away for finish_update() to index */ upd->kdb_entry_sno = cur_sno; i = (cur_sno - 1) % ulogentries; indx_log = (kdb_ent_header_t *)INDEX(ulog, i); (void) memset(indx_log, 0, ulog->kdb_block); indx_log->kdb_umagic = KDB_UMAGIC; indx_log->kdb_entry_size = upd_size; indx_log->kdb_entry_sno = cur_sno; indx_log->kdb_time = upd->kdb_time = ktime; indx_log->kdb_commit = upd->kdb_commit = FALSE; ulog->kdb_state = KDB_UNSTABLE; xdrmem_create(&xdrs, (char *)indx_log->entry_data, indx_log->kdb_entry_size, XDR_ENCODE); if (!xdr_kdb_incr_update_t(&xdrs, upd)) return (KRB5_LOG_CONV); if (retval = ulog_sync_update(ulog, indx_log)) return (retval); if (ulog->kdb_num < ulogentries) ulog->kdb_num++; ulog->kdb_last_sno = cur_sno; ulog->kdb_last_time = ktime; /* * Since this is a circular array, once we circled, kdb_first_sno is * always kdb_entry_sno + 1. */ if (cur_sno > ulogentries) { i = upd->kdb_entry_sno % ulogentries; indx_log = (kdb_ent_header_t *)INDEX(ulog, i); ulog->kdb_first_sno = indx_log->kdb_entry_sno; ulog->kdb_first_time = indx_log->kdb_time; } else if (cur_sno == 1) { ulog->kdb_first_sno = 1; ulog->kdb_first_time = indx_log->kdb_time; } ulog_sync_header(ulog); return (0); } /* * Mark the log entry as committed and sync the memory mapped log * to file. */ krb5_error_code ulog_finish_update(krb5_context context, kdb_incr_update_t *upd) { krb5_error_code retval; kdb_ent_header_t *indx_log; uint_t i; kdb_log_context *log_ctx; kdb_hlog_t *ulog = NULL; uint32_t ulogentries; INIT_ULOG(context); ulogentries = log_ctx->ulogentries; i = (upd->kdb_entry_sno - 1) % ulogentries; indx_log = (kdb_ent_header_t *)INDEX(ulog, i); indx_log->kdb_commit = TRUE; ulog->kdb_state = KDB_STABLE; if (retval = ulog_sync_update(ulog, indx_log)) return (retval); ulog_sync_header(ulog); return (0); } /* * Set the header log details on the slave and sync it to file. */ void ulog_finish_update_slave(kdb_hlog_t *ulog, kdb_last_t lastentry) { ulog->kdb_last_sno = lastentry.last_sno; ulog->kdb_last_time = lastentry.last_time; ulog_sync_header(ulog); } /* * Delete an entry to the update log. */ krb5_error_code ulog_delete_update(krb5_context context, kdb_incr_update_t *upd) { upd->kdb_deleted = TRUE; return (ulog_add_update(context, upd)); } /* * Used by the slave or master (during ulog_check) to update it's hash db from * the incr update log. */ krb5_error_code ulog_replay(krb5_context context, kdb_incr_result_t *incr_ret) { krb5_db_entry *entry = NULL; kdb_incr_update_t *upd = NULL, *fupd; int i, no_of_updates; krb5_error_code retval; krb5_principal dbprinc = NULL; kdb_last_t errlast; char *dbprincstr = NULL; kdb_log_context *log_ctx; kdb_hlog_t *ulog = NULL; bool_t fini = FALSE; INIT_ULOG(context); no_of_updates = incr_ret->updates.kdb_ulog_t_len; upd = incr_ret->updates.kdb_ulog_t_val; fupd = upd; /* * We reset last_sno and last_time to 0, if krb5_db_put_principal * or krb5_db_delete_principal fail. */ errlast.last_sno = (unsigned int)0; errlast.last_time.seconds = (unsigned int)0; errlast.last_time.useconds = (unsigned int)0; if (krb5_db_inited(context)) { retval = krb5_db_open(context, NULL, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN); if (retval != 0) goto cleanup; fini = TRUE; } for (i = 0; i < no_of_updates; i++) { int nentry = 1; if (!upd->kdb_commit) continue; if (upd->kdb_deleted) { dbprincstr = malloc((upd->kdb_princ_name.utf8str_t_len + 1) * sizeof (char)); if (dbprincstr == NULL) { retval = ENOMEM; goto cleanup; } (void) strlcpy(dbprincstr, (char *)upd->kdb_princ_name.utf8str_t_val, (upd->kdb_princ_name.utf8str_t_len + 1)); if (retval = krb5_parse_name(context, dbprincstr, &dbprinc)) { goto cleanup; } if (dbprincstr) free(dbprincstr); retval = krb5_db_delete_principal(context, dbprinc, &nentry); if (dbprinc) krb5_free_principal(context, dbprinc); if (retval) goto cleanup; } else { entry = (krb5_db_entry *)malloc(sizeof (krb5_db_entry)); if (!entry) { retval = errno; goto cleanup; } (void) memset(entry, 0, sizeof (krb5_db_entry)); if (retval = ulog_conv_2dbentry(context, entry, upd, 1)) goto cleanup; retval = krb5_db_put_principal(context, entry, &nentry); if (entry) { krb5_db_free_principal(context, entry, nentry); free(entry); entry = NULL; } if (retval) goto cleanup; } upd++; } cleanup: if (fupd) ulog_free_entries(fupd, no_of_updates); if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) { if (retval) ulog_finish_update_slave(ulog, errlast); else ulog_finish_update_slave(ulog, incr_ret->lastentry); } if (fini == TRUE) krb5_db_fini(context); return (retval); } /* * Validate the log file and resync any uncommitted update entries * to the principal database. */ krb5_error_code ulog_check(krb5_context context, kdb_hlog_t *ulog) { XDR xdrs; krb5_error_code retval = 0; int i; kdb_ent_header_t *indx_log; kdb_incr_update_t *upd = NULL; kdb_incr_result_t *incr_ret = NULL; ulog->kdb_state = KDB_STABLE; for (i = 0; i < ulog->kdb_num; i++) { indx_log = (kdb_ent_header_t *)INDEX(ulog, i); if (indx_log->kdb_umagic != KDB_UMAGIC) { /* * Update entry corrupted we should scream and die */ ulog->kdb_state = KDB_CORRUPT; retval = KRB5_LOG_CORRUPT; break; } if (indx_log->kdb_commit == FALSE) { ulog->kdb_state = KDB_UNSTABLE; incr_ret = (kdb_incr_result_t *) malloc(sizeof (kdb_incr_result_t)); if (incr_ret == NULL) { retval = errno; goto error; } upd = (kdb_incr_update_t *) malloc(sizeof (kdb_incr_update_t)); if (upd == NULL) { retval = errno; goto error; } (void) memset(upd, 0, sizeof (kdb_incr_update_t)); xdrmem_create(&xdrs, (char *)indx_log->entry_data, indx_log->kdb_entry_size, XDR_DECODE); if (!xdr_kdb_incr_update_t(&xdrs, upd)) { retval = KRB5_LOG_CONV; goto error; } incr_ret->updates.kdb_ulog_t_len = 1; incr_ret->updates.kdb_ulog_t_val = upd; upd->kdb_commit = TRUE; /* * We don't want to readd this update and just use the * existing update to be propagated later on */ ulog_set_role(context, IPROP_NULL); retval = ulog_replay(context, incr_ret); /* * upd was freed by ulog_replay, we NULL * the pointer in case we subsequently break from loop. */ upd = NULL; if (incr_ret) { free(incr_ret); incr_ret = NULL; } ulog_set_role(context, IPROP_MASTER); if (retval) goto error; /* * We flag this as committed since this was * the last entry before kadmind crashed, ergo * the slaves have not seen this update before */ indx_log->kdb_commit = TRUE; retval = ulog_sync_update(ulog, indx_log); if (retval) goto error; ulog->kdb_state = KDB_STABLE; } } error: if (upd) ulog_free_entries(upd, 1); if (incr_ret) free(incr_ret); ulog_sync_header(ulog); return (retval); } /* * Map the log file to memory for performance and simplicity. * * Called by: if iprop_enabled then ulog_map(); * Assumes that the caller will terminate on ulog_map, hence munmap and * closing of the fd are implicitly performed by the caller. * Returns 0 on success else failure. */ krb5_error_code ulog_map(krb5_context context, kadm5_config_params *params, int caller) { struct stat st; krb5_error_code retval; uint32_t ulog_filesize; char logname[MAX_FILENAME]; kdb_log_context *log_ctx; kdb_hlog_t *ulog = NULL; uint32_t ulogentries; int ulogfd = -1; if ((caller == FKADMIND) || (caller == FKCOMMAND)) ulogentries = params->iprop_ulogsize; ulog_filesize = sizeof (kdb_hlog_t); if (strlcpy(logname, params->dbname, MAX_FILENAME) >= MAX_FILENAME) return (KRB5_LOG_ERROR); if (strlcat(logname, ".ulog", MAX_FILENAME) >= MAX_FILENAME) return (KRB5_LOG_ERROR); if (stat(logname, &st) == -1) { if (caller == FKPROPLOG) { /* * File doesn't exist so we exit with kproplog */ return (errno); } if ((ulogfd = open(logname, O_RDWR+O_CREAT, 0600)) == -1) { return (errno); } if (lseek(ulogfd, 0L, SEEK_CUR) == -1) { return (errno); } if ((caller == FKADMIND) || (caller == FKCOMMAND)) ulog_filesize += ulogentries * ULOG_BLOCK; if (lseek(ulogfd, ulog_filesize, SEEK_SET) == -1) { return (errno); } if (write(ulogfd, "+", 1) != 1) { return (errno); } } else { if ((ulogfd = open(logname, O_RDWR, 0600)) == -1) { /* * Can't open existing log file */ return (errno); } } if (caller == FKPROPLOG) { fstat(ulogfd, &st); ulog_filesize = st.st_size; ulog = (kdb_hlog_t *)mmap(0, ulog_filesize, PROT_READ+PROT_WRITE, MAP_PRIVATE, ulogfd, 0); } else { /* * else kadmind, kpropd, & kcommands should udpate stores */ ulog = (kdb_hlog_t *)mmap(0, MAXLOGLEN, PROT_READ+PROT_WRITE, MAP_SHARED, ulogfd, 0); } if ((int)(ulog) == -1) { /* * Can't map update log file to memory */ return (errno); } if (!context->kdblog_context) { if (!(log_ctx = malloc(sizeof (kdb_log_context)))) return (errno); context->kdblog_context = (void *)log_ctx; } else log_ctx = context->kdblog_context; log_ctx->ulog = ulog; log_ctx->ulogentries = ulogentries; log_ctx->ulogfd = ulogfd; if (ulog->kdb_hmagic != KDB_HMAGIC) { if (ulog->kdb_hmagic == 0) { /* * New update log */ (void) memset(ulog, 0, sizeof (kdb_hlog_t)); ulog->kdb_hmagic = KDB_HMAGIC; ulog->db_version_num = KDB_VERSION; ulog->kdb_state = KDB_STABLE; ulog->kdb_block = ULOG_BLOCK; if (!(caller == FKPROPLOG)) ulog_sync_header(ulog); } else { return (KRB5_LOG_CORRUPT); } } if (caller == FKADMIND) { switch (ulog->kdb_state) { case KDB_STABLE: case KDB_UNSTABLE: /* * Log is currently un/stable, check anyway */ retval = ulog_check(context, ulog); if (retval == KRB5_LOG_CORRUPT) { return (retval); } break; case KDB_CORRUPT: return (KRB5_LOG_CORRUPT); default: /* * Invalid db state */ return (KRB5_LOG_ERROR); } } else if ((caller == FKPROPLOG) || (caller == FKPROPD)) { /* * kproplog and kpropd don't need to do anything else */ return (0); } /* * Reinit ulog if the log is being truncated or expanded after * we have circled. */ if (ulog->kdb_num != ulogentries) { if ((ulog->kdb_num != 0) && ((ulog->kdb_last_sno > ulog->kdb_num) || (ulog->kdb_num > ulogentries))) { (void) memset(ulog, 0, sizeof (kdb_hlog_t)); ulog->kdb_hmagic = KDB_HMAGIC; ulog->db_version_num = KDB_VERSION; ulog->kdb_state = KDB_STABLE; ulog->kdb_block = ULOG_BLOCK; ulog_sync_header(ulog); } /* * Expand ulog if we have specified a greater size */ if (ulog->kdb_num < ulogentries) { ulog_filesize += ulogentries * ulog->kdb_block; if (lseek(ulogfd, ulog_filesize, SEEK_SET) == -1) { return (errno); } if (write(ulogfd, "+", 1) != 1) { return (errno); } } } return (0); } /* * Get the last set of updates seen, (last+1) to n is returned. */ krb5_error_code ulog_get_entries( krb5_context context, /* input - krb5 lib config */ kdb_last_t last, /* input - slave's last sno */ kdb_incr_result_t *ulog_handle) /* output - incr result for slave */ { XDR xdrs; kdb_ent_header_t *indx_log; kdb_incr_update_t *upd; uint_t indx, count, tdiff; uint32_t sno; krb5_error_code retval; struct timeval timestamp; kdb_log_context *log_ctx; kdb_hlog_t *ulog = NULL; uint32_t ulogentries; INIT_ULOG(context); ulogentries = log_ctx->ulogentries; /* * Check to make sure we don't have a corrupt ulog first. */ if (ulog->kdb_state == KDB_CORRUPT) { ulog_handle->ret = UPDATE_ERROR; return (KRB5_LOG_CORRUPT); } gettimeofday(×tamp, NULL); tdiff = timestamp.tv_sec - ulog->kdb_last_time.seconds; if (tdiff <= ULOG_IDLE_TIME) { ulog_handle->ret = UPDATE_BUSY; return (0); } /* * We need to lock out other processes here, such as kadmin.local, * since we are looking at the last_sno and looking up updates. So * we can share with other readers. */ retval = krb5_db_lock(context, KRB5_LOCKMODE_SHARED); if (retval) return (retval); /* * We may have overflowed the update log or we shrunk the log, or * the client's ulog has just been created. */ if ((last.last_sno > ulog->kdb_last_sno) || (last.last_sno < ulog->kdb_first_sno) || (last.last_sno == 0)) { ulog_handle->lastentry.last_sno = ulog->kdb_last_sno; (void) krb5_db_unlock(context); ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED; return (0); } else if (last.last_sno <= ulog->kdb_last_sno) { sno = last.last_sno; indx = (sno - 1) % ulogentries; indx_log = (kdb_ent_header_t *)INDEX(ulog, indx); /* * Validate the time stamp just to make sure it was the same sno */ if ((indx_log->kdb_time.seconds == last.last_time.seconds) && (indx_log->kdb_time.useconds == last.last_time.useconds)) { /* * If we have the same sno we return success */ if (last.last_sno == ulog->kdb_last_sno) { (void) krb5_db_unlock(context); ulog_handle->ret = UPDATE_NIL; return (0); } count = ulog->kdb_last_sno - sno; ulog_handle->updates.kdb_ulog_t_val = (kdb_incr_update_t *)malloc( sizeof (kdb_incr_update_t) * count); upd = ulog_handle->updates.kdb_ulog_t_val; if (upd == NULL) { (void) krb5_db_unlock(context); ulog_handle->ret = UPDATE_ERROR; return (errno); } while (sno < ulog->kdb_last_sno) { indx = sno % ulogentries; indx_log = (kdb_ent_header_t *) INDEX(ulog, indx); (void) memset(upd, 0, sizeof (kdb_incr_update_t)); xdrmem_create(&xdrs, (char *)indx_log->entry_data, indx_log->kdb_entry_size, XDR_DECODE); if (!xdr_kdb_incr_update_t(&xdrs, upd)) { (void) krb5_db_unlock(context); ulog_handle->ret = UPDATE_ERROR; return (KRB5_LOG_CONV); } /* * Mark commitment since we didn't * want to decode and encode the * incr update record the first time. */ upd->kdb_commit = indx_log->kdb_commit; upd++; sno++; } /* while */ ulog_handle->updates.kdb_ulog_t_len = count; ulog_handle->lastentry.last_sno = ulog->kdb_last_sno; ulog_handle->lastentry.last_time.seconds = ulog->kdb_last_time.seconds; ulog_handle->lastentry.last_time.useconds = ulog->kdb_last_time.useconds; ulog_handle->ret = UPDATE_OK; (void) krb5_db_unlock(context); return (0); } else { /* * We have time stamp mismatch or we no longer have * the slave's last sno, so we brute force it */ (void) krb5_db_unlock(context); ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED; return (0); } } /* * Should never get here, return error */ ulog_handle->ret = UPDATE_ERROR; return (KRB5_LOG_ERROR); } krb5_error_code ulog_set_role(krb5_context ctx, iprop_role role) { kdb_log_context *log_ctx; if (!ctx->kdblog_context) { if (!(log_ctx = malloc(sizeof (kdb_log_context)))) return (errno); ctx->kdblog_context = (void *)log_ctx; } else log_ctx = ctx->kdblog_context; log_ctx->iproprole = role; return (0); } /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KDB_LOG_H #define _KDB_LOG_H #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* * DB macros */ #define INDEX(ulogaddr, i) ((ulong_t) ulogaddr + sizeof (kdb_hlog_t) + \ (i*ulog->kdb_block)) /* * Current DB version # */ #define KDB_VERSION 1 /* * DB log states */ #define KDB_STABLE 1 #define KDB_UNSTABLE 2 #define KDB_CORRUPT 3 /* * DB log constants */ #define KDB_UMAGIC 0x6661212 #define KDB_HMAGIC 0x6662323 /* * DB Flags */ #define FKADMIND 1 #define FKPROPLOG 2 #define FKPROPD 3 #define FKCOMMAND 4 /* Includes kadmin.local and kdb5_util */ /* * Default ulog file attributes */ #define ULOG_FILE "/var/krb5/principal.ulog" #define MAX_FILENAME (PATH_MAX + 1) #define MAX_ULOGENTRIES 2500 #define DEF_ULOGENTRIES 1000 #define ULOG_IDLE_TIME 10 /* in seconds */ /* * Max size of update entry + update header * We make this large since resizing can be costly. */ #define ULOG_BLOCK 2048 /* Default size of principal record */ #define MAXLOGLEN 0x10000000 /* 256 MB log file */ /* * Prototype declarations */ extern krb5_error_code ulog_map(krb5_context context, kadm5_config_params *params, int caller); extern krb5_error_code ulog_add_update(krb5_context context, kdb_incr_update_t *upd); extern krb5_error_code ulog_delete_update(krb5_context context, kdb_incr_update_t *upd); extern krb5_error_code ulog_finish_update(krb5_context context, kdb_incr_update_t *upd); extern krb5_error_code ulog_get_entries(krb5_context context, kdb_last_t last, kdb_incr_result_t *ulog_handle); extern krb5_error_code ulog_replay(krb5_context context, kdb_incr_result_t *incr_ret); extern krb5_error_code ulog_conv_2logentry(krb5_context context, krb5_db_entry *entries, kdb_incr_update_t *updates, int nentries); extern krb5_error_code ulog_conv_2dbentry(krb5_context context, krb5_db_entry *entries, kdb_incr_update_t *updates, int nentries); extern void ulog_free_entries(kdb_incr_update_t *updates, int no_of_updates); extern krb5_error_code ulog_set_role(krb5_context ctx, iprop_role role); typedef struct kdb_hlog { uint32_t kdb_hmagic; /* Log header magic # */ uint16_t db_version_num; /* Kerberos database version no. */ uint32_t kdb_num; /* # of updates in log */ kdbe_time_t kdb_first_time; /* Timestamp of first update */ kdbe_time_t kdb_last_time; /* Timestamp of last update */ kdb_sno_t kdb_first_sno; /* First serial # in the update log */ kdb_sno_t kdb_last_sno; /* Last serial # in the update log */ uint16_t kdb_state; /* State of update log */ uint16_t kdb_block; /* Block size of each element */ } kdb_hlog_t; typedef struct kdb_ent_header { uint32_t kdb_umagic; /* Update entry magic # */ kdb_sno_t kdb_entry_sno; /* Serial # of entry */ kdbe_time_t kdb_time; /* Timestamp of update */ bool_t kdb_commit; /* Is the entry committed or not */ uint32_t kdb_entry_size; /* Size of update entry */ uchar_t entry_data[4]; /* Address of kdb_incr_update_t */ } kdb_ent_header_t; typedef struct _kdb_log_context { iprop_role iproprole; kdb_hlog_t *ulog; uint32_t ulogentries; int ulogfd; } kdb_log_context; #ifdef __cplusplus } #endif #endif /* !_KDB_LOG_H */ /* * kadmin/v5server/keytab.c * * Copyright 1995 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ /* * Copyright 2015 OmniTI Computer Consulting, Inc. All rights reserved. * Copyright 2024 Toomas Soome */ #include #include "k5-int.h" #include "kdb_kt.h" static int is_xrealm_tgt(krb5_context, krb5_const_principal); krb5_error_code krb5_ktkdb_close (krb5_context, krb5_keytab); krb5_error_code krb5_ktkdb_get_entry (krb5_context, krb5_keytab, krb5_const_principal, krb5_kvno, krb5_enctype, krb5_keytab_entry *); static krb5_error_code krb5_ktkdb_get_name(krb5_context context, krb5_keytab keytab, char *name, unsigned int namelen) { if (namelen < sizeof("KDB:")) return KRB5_KT_NAME_TOOLONG; strcpy(name, "KDB:"); return 0; } krb5_kt_ops krb5_kt_kdb_ops = { 0, "KDB", /* Prefix -- this string should not appear anywhere else! */ krb5_ktkdb_resolve, /* resolve */ krb5_ktkdb_get_name, /* get_name */ krb5_ktkdb_close, /* close */ krb5_ktkdb_get_entry, /* get */ NULL, /* start_seq_get */ NULL, /* get_next */ NULL, /* end_get */ NULL, /* add (extended) */ NULL, /* remove (extended) */ NULL, /* (void *) &krb5_ktfile_ser_entry */ }; typedef struct krb5_ktkdb_data { char *name; } krb5_ktkdb_data; krb5_error_code krb5_ktkdb_resolve(krb5_context context, const char *name, krb5_keytab *id) { krb5_ktkdb_data *data; krb5_keytab kt; kt = malloc(sizeof (*kt)); if (kt == NULL) return (ENOMEM); kt->ops = &krb5_kt_kdb_ops; kt->magic = KV5M_KEYTAB; data = calloc(1, sizeof (*data)); if (data == NULL) goto fail; if (name != NULL) { data->name = strdup(name); if (data->name == NULL) goto fail; } kt->data = data; *id = kt; return (0); fail: krb5_xfree(data); krb5_xfree(kt); return (ENOMEM); } krb5_error_code krb5_ktkdb_close(krb5_context context, krb5_keytab kt) { krb5_ktkdb_data *data; /* * This routine is responsible for freeing all memory allocated * for this keytab. There are no system resources that need * to be freed nor are there any open files. * * This routine should undo anything done by krb5_ktkdb_resolve(). */ kt->ops = NULL; data = kt->data; krb5_xfree(data->name); krb5_xfree(data); krb5_xfree(kt); return (0); } static krb5_context ktkdb_ctx = NULL; /* * Set a different context for use with ktkdb_get_entry(). This is * primarily useful for kadmind, where the gssapi library context, * which will be used for the keytab, will necessarily have a * different context than that used by the kadm5 library to access the * database for its own purposes. */ krb5_error_code krb5_ktkdb_set_context(krb5_context ctx) { ktkdb_ctx = ctx; return 0; } krb5_error_code krb5_ktkdb_get_entry(in_context, id, principal, kvno, enctype, entry) krb5_context in_context; krb5_keytab id; krb5_const_principal principal; krb5_kvno kvno; krb5_enctype enctype; krb5_keytab_entry *entry; { krb5_context context; krb5_keyblock *master_key; krb5_error_code kerror = 0; krb5_key_data *key_data; krb5_db_entry db_entry; krb5_boolean more = 0; int n = 0; int xrealm_tgt; krb5_boolean similar; if (ktkdb_ctx) context = ktkdb_ctx; else context = in_context; xrealm_tgt = is_xrealm_tgt(context, principal); /* Open database */ /* krb5_db_init(context); */ if ((kerror = krb5_db_inited(context))) return(kerror); /* get_principal */ kerror = krb5_db_get_principal(context, principal, & db_entry, &n, &more); if (kerror) { /* krb5_db_close_database(context); */ return(kerror); } if (n != 1) { /* krb5_db_close_database(context); */ return KRB5_KT_NOTFOUND; } if (db_entry.attributes & KRB5_KDB_DISALLOW_SVR || db_entry.attributes & KRB5_KDB_DISALLOW_ALL_TIX) { kerror = KRB5_KT_NOTFOUND; goto error; } /* match key */ kerror = krb5_db_get_mkey(context, &master_key); if (kerror) goto error; /* For cross realm tgts, we match whatever enctype is provided; * for other principals, we only match the first enctype that is * found. Since the TGS and AS code do the same thing, then we * will only successfully decrypt tickets we have issued.*/ kerror = krb5_dbe_find_enctype(context, &db_entry, xrealm_tgt?enctype:-1, -1, kvno, &key_data); if (kerror) goto error; kerror = krb5_dbekd_decrypt_key_data(context, master_key, key_data, &entry->key, NULL); if (kerror) goto error; if (enctype > 0) { kerror = krb5_c_enctype_compare(context, enctype, entry->key.enctype, &similar); if (kerror) goto error; if (!similar) { kerror = KRB5_KDB_NO_PERMITTED_KEY; goto error; } } /* * Coerce the enctype of the output keyblock in case we got an * inexact match on the enctype. */ entry->key.enctype = enctype; kerror = krb5_copy_principal(context, principal, &entry->principal); if (kerror) goto error; /* Close database */ error: krb5_db_free_principal(context, &db_entry, 1); /* krb5_db_close_database(context); */ return(kerror); } /* * is_xrealm_tgt: Returns true if the principal is a cross-realm TGT * principal-- a principal with first component krbtgt and second * component not equal to realm. */ static int is_xrealm_tgt(krb5_context context, krb5_const_principal princ) { krb5_data *dat; if (krb5_princ_size(context, princ) != 2) return 0; dat = krb5_princ_component(context, princ, 0); if (strncmp("krbtgt", dat->data, dat->length) != 0) return 0; dat = krb5_princ_component(context, princ, 1); if (dat->length != princ->realm.length) return 1; if (strncmp(dat->data, princ->realm.data, dat->length) == 0) return 0; return 1; } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # # Due to mistakes made early in the history of this library, there are # no SUNW_1.1 symbols, but the version is now kept as a placeholder. # Don't add any symbols to this version. $mapfile_version 2 SYMBOL_VERSION SUNW_1.1 { global: SUNW_1.1; }; SYMBOL_VERSION SUNWprivate_1.1 { global: krb5_db_alloc; krb5_db_create; krb5_db_create_policy; krb5_db_delete_policy; krb5_db_delete_principal; krb5_db_destroy; krb5_db_fetch_mkey; krb5_db_fini; krb5_db_free; krb5_db_free_policy; krb5_db_free_principal; krb5_db_get_age; krb5_db_get_mkey; krb5_db_get_policy; krb5_db_get_principal; krb5_db_get_principal_nolock; krb5_db_inited; krb5_db_iterate; krb5_db_iter_policy; krb5_db_lock; krb5_db_open; krb5_db_promote; krb5_db_put_principal; krb5_db_put_policy; krb5_db_set_mkey; krb5_db_setup_mkey_name; krb5_db_store_master_key; krb5_db_supports_iprop; krb5_db_unlock; krb5_db_verify_master_key; krb5_dbe_apw; krb5_dbe_ark; krb5_dbe_cpw; krb5_dbe_create_key_data; krb5_dbe_crk; krb5_dbe_find_enctype; krb5_dbe_lookup_last_pwd_change; krb5_dbe_lookup_mod_princ_data; krb5_dbe_lookup_tl_data; krb5_dbe_search_enctype; krb5_dbe_update_last_pwd_change; krb5_dbe_update_mod_princ_data; krb5_dbe_update_tl_data; krb5_dbekd_decrypt_key_data; krb5_dbekd_encrypt_key_data; krb5_def_store_mkey; krb5_kt_kdb_ops { ASSERT = { TYPE = OBJECT; SIZE = 48; }; }; krb5_ktkdb_close; krb5_ktkdb_get_entry; krb5_ktkdb_resolve; krb5_ktkdb_set_context; krb5_mkey_pwd_prompt1 { ASSERT = { TYPE = OBJECT; SIZE = 4; }; }; krb5_mkey_pwd_prompt2 { ASSERT = { TYPE = OBJECT; SIZE = 4; }; }; ulog_add_update; ulog_conv_2logentry; ulog_delete_update; ulog_finish_update; ulog_free_entries; ulog_get_entries; ulog_map; ulog_replay; ulog_set_role; local: *; }; # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # include global definitions include ../../../Makefile.master # # Build everything in parallel; use .WAIT for dependencies .PARALLEL: SUBDIRS= \ kdb \ preauth all : TARGET= all install : TARGET= install clean : TARGET= clean clobber : TARGET= clobber _msg : TARGET= _msg .KEEP_STATE: all install clean clobber _msg: $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # include global definitions include ../../../../Makefile.master # # Build everything in parallel; use .WAIT for dependencies .PARALLEL: # Hammerhead: ldap KDB plugin removed (libsldap chain removal, Phase 1). SUBDIRS= \ db2 all : TARGET= all install : TARGET= install clean : TARGET= clean clobber : TARGET= clobber _msg : TARGET= _msg .KEEP_STATE: all install clean clobber _msg: $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include $(SRC)/lib/krb5/Makefile.lib include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 # Hammerhead: amd64-only SUBDIRS= \ libdb2 \ $(MACH64) all : TARGET= all clean : TARGET= clean clobber : TARGET= clobber install : TARGET= install _msg : TARGET= _msg .KEEP_STATE: # Hammerhead: amd64/db2.so.1 links with -ldb2, so libdb2 must install first $(MACH64): libdb2 all clean clobber _msg: $(SUBDIRS) $(ROOTLIBDIR): $(INS.dir) $(ROOTLIBDIR)/%: % $(INS.file) install: $(ROOTLIBDIR) $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. LIBRARY= db2.a VERS= .1 # XXX need to set the install path to plugin dir # db2 plugin objects DB2_OBJS= \ adb_openclose.o \ adb_policy.o \ db2_exp.o \ kdb_db2.o \ kdb_xdr.o \ pol_xdr.o OBJECTS= $(DB2_OBJS) # include library definitions include $(SRC)/lib/krb5/Makefile.lib LIBS= $(DYNLIB) SRCS= $(DB2_OBJS:%.o=../%.c) include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 POFILE = $(LIBRARY:%.a=%.po) POFILES = generic.po #override liblink INS.liblink= -$(RM) $@; $(SYMLINK) $(LIBLINKS)$(VERS) $@ CPPFLAGS += -DHAVE_CONFIG_H -DHAVE_BT_RSEQ \ -I$(SRC)/cmd/krb5/iprop \ -I$(SRC)/lib/krb5 \ -I$(SRC)/lib/krb5/kdb \ -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/lib/gss_mechs/mech_krb5/krb5/os \ -I$(SRC)/lib/gss_mechs/mech_krb5/include/krb5 \ -I$(SRC)/uts/common/gssapi/include/ \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include CFLAGS += $(CCVERBOSE) CERRWARN += -Wno-unused-variable CERRWARN += -Wno-unused-function CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) # not linted SMATCH=off DYNFLAGS += $(KRUNPATH) $(KERBRUNPATH) $(KMECHLIB) # Hammerhead: GNU ld needs rpath-link for transitive deps (libkdb, libdyn) via libkadm5srv DYNFLAGS += -Wl,-rpath-link,$(ROOT)/usr/lib/krb5 LDLIBS += -L $(ROOTLIBDIR) -ldb2 -lkdb -lkadm5srv -lc -lnsl .KEEP_STATE: all: $(LIBS) # include library targets include $(SRC)/lib/krb5/Makefile.targ FRC: generic.po: FRC $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext ../*.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved */ #include #include #include #include #include "policy_db.h" #include #include #define MAX_LOCK_TRIES 5 struct _locklist { osa_adb_lock_ent lockinfo; struct _locklist *next; }; krb5_error_code osa_adb_create_db(char *filename, char *lockfilename, int magic) { int lf; DB *db; BTREEINFO btinfo; memset(&btinfo, 0, sizeof(btinfo)); btinfo.flags = 0; btinfo.cachesize = 0; btinfo.psize = 4096; btinfo.lorder = 0; btinfo.minkeypage = 0; btinfo.compare = NULL; btinfo.prefix = NULL; db = dbopen(filename, O_RDWR | O_CREAT | O_EXCL, 0600, DB_BTREE, &btinfo); if (db == NULL) return errno; if (db->close(db) < 0) return errno; /* only create the lock file if we successfully created the db */ lf = THREEPARAMOPEN(lockfilename, O_RDWR | O_CREAT | O_EXCL, 0600); if (lf == -1) return errno; (void) close(lf); return OSA_ADB_OK; } krb5_error_code osa_adb_destroy_db(char *filename, char *lockfilename, int magic) { /* the admin databases do not contain security-critical data */ if (unlink(filename) < 0 || unlink(lockfilename) < 0) return errno; return OSA_ADB_OK; } krb5_error_code osa_adb_rename_db(char *filefrom, char *lockfrom, char *fileto, char *lockto, int magic) { osa_adb_db_t fromdb, todb; krb5_error_code ret; /* make sure todb exists */ /*LINTED*/ if ((ret = osa_adb_create_db(fileto, lockto, magic)) && ret != EEXIST) return ret; if ((ret = osa_adb_init_db(&fromdb, filefrom, lockfrom, magic))) return ret; if ((ret = osa_adb_init_db(&todb, fileto, lockto, magic))) { (void) osa_adb_fini_db(fromdb, magic); return ret; } if ((ret = osa_adb_get_lock(fromdb, KRB5_DB_LOCKMODE_PERMANENT))) { (void) osa_adb_fini_db(fromdb, magic); (void) osa_adb_fini_db(todb, magic); return ret; } if ((ret = osa_adb_get_lock(todb, KRB5_DB_LOCKMODE_PERMANENT))) { (void) osa_adb_fini_db(fromdb, magic); (void) osa_adb_fini_db(todb, magic); return ret; } if ((rename(filefrom, fileto) < 0)) { (void) osa_adb_fini_db(fromdb, magic); (void) osa_adb_fini_db(todb, magic); return errno; } /* * Do not release the lock on fromdb because it is being renamed * out of existence; no one can ever use it again. */ if ((ret = osa_adb_release_lock(todb))) { (void) osa_adb_fini_db(fromdb, magic); (void) osa_adb_fini_db(todb, magic); return ret; } (void) osa_adb_fini_db(fromdb, magic); (void) osa_adb_fini_db(todb, magic); return 0; } krb5_error_code osa_adb_init_db(osa_adb_db_t *dbp, char *filename, char *lockfilename, int magic) { osa_adb_db_t db; static struct _locklist *locklist = NULL; struct _locklist *lockp; krb5_error_code code; if (dbp == NULL || filename == NULL) return EINVAL; db = (osa_adb_princ_t) malloc(sizeof(osa_adb_db_ent)); if (db == NULL) return ENOMEM; memset(db, 0, sizeof(*db)); db->info.hash = NULL; db->info.bsize = 256; db->info.ffactor = 8; db->info.nelem = 25000; db->info.lorder = 0; db->btinfo.flags = 0; db->btinfo.cachesize = 0; db->btinfo.psize = 4096; db->btinfo.lorder = 0; db->btinfo.minkeypage = 0; db->btinfo.compare = NULL; db->btinfo.prefix = NULL; /* * A process is allowed to open the same database multiple times * and access it via different handles. If the handles use * distinct lockinfo structures, things get confused: lock(A), * lock(B), release(B) will result in the kernel unlocking the * lock file but handle A will still think the file is locked. * Therefore, all handles using the same lock file must share a * single lockinfo structure. * * It is not sufficient to have a single lockinfo structure, * however, because a single process may also wish to open * multiple different databases simultaneously, with different * lock files. This code used to use a single static lockinfo * structure, which means that the second database opened used * the first database's lock file. This was Bad. * * We now maintain a linked list of lockinfo structures, keyed by * lockfilename. An entry is added when this function is called * with a new lockfilename, and all subsequent calls with that * lockfilename use the existing entry, updating the refcnt. * When the database is closed with fini_db(), the refcnt is * decremented, and when it is zero the lockinfo structure is * freed and reset. The entry in the linked list, however, is * never removed; it will just be reinitialized the next time * init_db is called with the right lockfilename. */ /* find or create the lockinfo structure for lockfilename */ lockp = locklist; while (lockp) { if (strcmp(lockp->lockinfo.filename, lockfilename) == 0) break; else lockp = lockp->next; } if (lockp == NULL) { /* doesn't exist, create it, add to list */ lockp = (struct _locklist *) malloc(sizeof(*lockp)); if (lockp == NULL) { free(db); return ENOMEM; } memset(lockp, 0, sizeof(*lockp)); lockp->next = locklist; locklist = lockp; } /* now initialize lockp->lockinfo if necessary */ if (lockp->lockinfo.lockfile == NULL) { if ((code = krb5int_init_context_kdc(&lockp->lockinfo.context))) { free(db); return((krb5_error_code) code); } /* * needs be open read/write so that write locking can work with * POSIX systems */ lockp->lockinfo.filename = strdup(lockfilename); if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r+F")) == NULL) { /* * maybe someone took away write permission so we could only * get shared locks? */ if ((lockp->lockinfo.lockfile = fopen(lockfilename, "rF")) == NULL) { free(db); return OSA_ADB_NOLOCKFILE; } } lockp->lockinfo.lockmode = lockp->lockinfo.lockcnt = 0; } /* lockp is set, lockinfo is initialized, update the reference count */ db->lock = &lockp->lockinfo; db->lock->refcnt++; db->opencnt = 0; db->filename = strdup(filename); db->magic = magic; *dbp = db; return OSA_ADB_OK; } krb5_error_code osa_adb_fini_db(osa_adb_db_t db, int magic) { if (db->magic != magic) return EINVAL; if (db->lock->refcnt == 0) { /* barry says this can't happen */ return OSA_ADB_FAILURE; } else { db->lock->refcnt--; } if (db->lock->refcnt == 0) { /* * Don't free db->lock->filename, it is used as a key to * find the lockinfo entry in the linked list. If the * lockfile doesn't exist, we must be closing the database * after trashing it. This has to be allowed, so don't * generate an error. */ if (db->lock->lockmode != KRB5_DB_LOCKMODE_PERMANENT) (void) fclose(db->lock->lockfile); db->lock->lockfile = NULL; krb5_free_context(db->lock->context); } db->magic = 0; free(db->filename); free(db); return OSA_ADB_OK; } krb5_error_code osa_adb_get_lock(osa_adb_db_t db, int mode) { int tries, gotlock, perm, krb5_mode, ret = 0; if (db->lock->lockmode >= mode) { /* No need to upgrade lock, just incr refcnt and return */ db->lock->lockcnt++; return(OSA_ADB_OK); } perm = 0; switch (mode) { case KRB5_DB_LOCKMODE_PERMANENT: perm = 1; /* FALLTHROUGH */ case KRB5_DB_LOCKMODE_EXCLUSIVE: krb5_mode = KRB5_LOCKMODE_EXCLUSIVE; break; case KRB5_DB_LOCKMODE_SHARED: krb5_mode = KRB5_LOCKMODE_SHARED; break; default: return(EINVAL); } for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) { if ((ret = krb5_lock_file(db->lock->context, fileno(db->lock->lockfile), krb5_mode|KRB5_LOCKMODE_DONTBLOCK)) == 0) { gotlock++; break; } else if (ret == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE) /* tried to exclusive-lock something we don't have */ /* write access to */ return OSA_ADB_NOEXCL_PERM; sleep(1); } /* test for all the likely "can't get lock" error codes */ if (ret == EACCES || ret == EAGAIN || ret == EWOULDBLOCK) return OSA_ADB_CANTLOCK_DB; else if (ret != 0) return ret; /* * If the file no longer exists, someone acquired a permanent * lock. If that process terminates its exclusive lock is lost, * but if we already had the file open we can (probably) lock it * even though it has been unlinked. So we need to insist that * it exist. */ if (access(db->lock->filename, F_OK) < 0) { (void) krb5_lock_file(db->lock->context, fileno(db->lock->lockfile), KRB5_LOCKMODE_UNLOCK); return OSA_ADB_NOLOCKFILE; } /* we have the shared/exclusive lock */ if (perm) { if (unlink(db->lock->filename) < 0) { /* somehow we can't delete the file, but we already */ /* have the lock, so release it and return */ ret = errno; (void) krb5_lock_file(db->lock->context, fileno(db->lock->lockfile), KRB5_LOCKMODE_UNLOCK); /* maybe we should return CANTLOCK_DB.. but that would */ /* look just like the db was already locked */ return ret; } /* this releases our exclusive lock.. which is okay because */ /* now no one else can get one either */ (void) fclose(db->lock->lockfile); } db->lock->lockmode = mode; db->lock->lockcnt++; return OSA_ADB_OK; } krb5_error_code osa_adb_release_lock(osa_adb_db_t db) { int ret, fd; if (!db->lock->lockcnt) /* lock already unlocked */ return OSA_ADB_NOTLOCKED; if (--db->lock->lockcnt == 0) { if (db->lock->lockmode == KRB5_DB_LOCKMODE_PERMANENT) { /* now we need to create the file since it does not exist */ fd = THREEPARAMOPEN(db->lock->filename,O_RDWR | O_CREAT | O_EXCL, 0600); if ((db->lock->lockfile = fdopen(fd, "w+F")) == NULL) return OSA_ADB_NOLOCKFILE; } else if ((ret = krb5_lock_file(db->lock->context, fileno(db->lock->lockfile), KRB5_LOCKMODE_UNLOCK))) return ret; db->lock->lockmode = 0; } return OSA_ADB_OK; } krb5_error_code osa_adb_open_and_lock(osa_adb_princ_t db, int locktype) { int ret; ret = osa_adb_get_lock(db, locktype); if (ret != OSA_ADB_OK) return ret; if (db->opencnt) goto open_ok; db->db = dbopen(db->filename, O_RDWR, 0600, DB_BTREE, &db->btinfo); if (db->db != NULL) goto open_ok; switch (errno) { #ifdef EFTYPE case EFTYPE: #endif case EINVAL: db->db = dbopen(db->filename, O_RDWR, 0600, DB_HASH, &db->info); if (db->db != NULL) goto open_ok; /* FALLTHROUGH */ default: (void) osa_adb_release_lock(db); if (errno == EINVAL) return OSA_ADB_BAD_DB; return errno; } open_ok: db->opencnt++; return OSA_ADB_OK; } krb5_error_code osa_adb_close_and_unlock(osa_adb_princ_t db) { if (--db->opencnt) return osa_adb_release_lock(db); if(db->db != NULL && db->db->close(db->db) == -1) { (void) osa_adb_release_lock(db); return OSA_ADB_FAILURE; } db->db = NULL; return(osa_adb_release_lock(db)); } /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * * $Header$ */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header$"; #endif #include #include #include "policy_db.h" #include #include #include extern caddr_t xdralloc_getdata(XDR *xdrs); extern void xdralloc_create(XDR *xdrs, enum xdr_op op); #define OPENLOCK(db, mode) \ { \ int olret; \ if (db == NULL) \ return EINVAL; \ else if (db->magic != OSA_ADB_POLICY_DB_MAGIC) \ return OSA_ADB_DBINIT; \ else if ((olret = osa_adb_open_and_lock(db, mode)) != OSA_ADB_OK) \ return olret; \ } #define CLOSELOCK(db) \ { \ int cl_ret; \ if ((cl_ret = osa_adb_close_and_unlock(db)) != OSA_ADB_OK) \ return cl_ret; \ } /* * Function: osa_adb_create_policy * * Purpose: create a policy entry in the policy db. * * Arguments: * entry (input) pointer to the entry to be added * OSA_ADB_OK on success, else error code. * * Requires: * entry have a valid name. * * Effects: * creates the entry in the db * * Modifies: * the policy db. * */ krb5_error_code osa_adb_create_policy(osa_adb_policy_t db, osa_policy_ent_t entry) { DBT dbkey; DBT dbdata; XDR xdrs; int ret; OPENLOCK(db, KRB5_DB_LOCKMODE_EXCLUSIVE); if(entry->name == NULL) { ret = EINVAL; goto error; } dbkey.data = entry->name; dbkey.size = (strlen(entry->name) + 1); switch(db->db->get(db->db, &dbkey, &dbdata, 0)) { case 0: ret = OSA_ADB_DUP; goto error; case 1: break; default: ret = errno; goto error; } xdralloc_create(&xdrs, XDR_ENCODE); if(!xdr_osa_policy_ent_rec(&xdrs, entry)) { xdr_destroy(&xdrs); ret = OSA_ADB_XDR_FAILURE; goto error; } dbdata.data = xdralloc_getdata(&xdrs); dbdata.size = xdr_getpos(&xdrs); switch(db->db->put(db->db, &dbkey, &dbdata, R_NOOVERWRITE)) { case 0: if((db->db->sync(db->db, 0)) == -1) ret = OSA_ADB_FAILURE; ret = OSA_ADB_OK; break; case 1: ret = OSA_ADB_DUP; break; default: ret = OSA_ADB_FAILURE; break; } xdr_destroy(&xdrs); error: CLOSELOCK(db); return ret; } /* * Function: osa_adb_destroy_policy * * Purpose: destroy a policy entry * * Arguments: * db (input) database handle * name (input) name of policy * OSA_ADB_OK on success, or error code. * * Requires: * db being valid. * name being non-null. * Effects: * deletes policy from db. * * Modifies: * policy db. * */ krb5_error_code osa_adb_destroy_policy(osa_adb_policy_t db, char *name) { DBT dbkey; int status, ret; OPENLOCK(db, KRB5_DB_LOCKMODE_EXCLUSIVE); if(name == NULL) { ret = EINVAL; goto error; } dbkey.data = name; dbkey.size = (strlen(name) + 1); status = db->db->del(db->db, &dbkey, 0); switch(status) { case 1: ret = OSA_ADB_NOENT; goto error; case 0: if ((db->db->sync(db->db, 0)) == -1) { ret = OSA_ADB_FAILURE; goto error; } ret = OSA_ADB_OK; break; default: ret = OSA_ADB_FAILURE; goto error; } error: CLOSELOCK(db); return ret; } /* * Function: osa_adb_get_policy * * Purpose: retrieve policy * * Arguments: * db (input) db handle * name (input) name of policy * entry (output) policy entry * cnt (inout) Number of entries * 0 on success, error code on failure. * * Requires: * Effects: * Modifies: */ krb5_error_code osa_adb_get_policy(osa_adb_policy_t db, char *name, osa_policy_ent_t *entry, int *cnt) { DBT dbkey; DBT dbdata; XDR xdrs; int ret; char *aligned_data; OPENLOCK(db, KRB5_DB_LOCKMODE_SHARED); *cnt = 1; if(name == NULL) { ret = EINVAL; goto error; } dbkey.data = name; dbkey.size = (strlen(dbkey.data) + 1); dbdata.data = NULL; dbdata.size = 0; switch((db->db->get(db->db, &dbkey, &dbdata, 0))) { case 1: ret = OSA_ADB_OK; *cnt = 0; goto error; case 0: break; default: ret = OSA_ADB_FAILURE; goto error; } if (!(*(entry) = (osa_policy_ent_t)malloc(sizeof(osa_policy_ent_rec)))) { ret = ENOMEM; goto error; } if (!(aligned_data = (char *) malloc(dbdata.size))) { ret = ENOMEM; goto error; } memcpy(aligned_data, dbdata.data, dbdata.size); memset(*entry, 0, sizeof(osa_policy_ent_rec)); xdrmem_create(&xdrs, aligned_data, dbdata.size, XDR_DECODE); if (!xdr_osa_policy_ent_rec(&xdrs, *entry)) ret = OSA_ADB_FAILURE; else ret = OSA_ADB_OK; xdr_destroy(&xdrs); free(aligned_data); error: CLOSELOCK(db); return ret; } /* * Function: osa_adb_put_policy * * Purpose: update a policy in the dababase * * Arguments: * db (input) db handle * entry (input) policy entry * 0 on success error code on failure. * * Requires: * [requires] * * Effects: * [effects] * * Modifies: * [modifies] * */ krb5_error_code osa_adb_put_policy(osa_adb_policy_t db, osa_policy_ent_t entry) { DBT dbkey; DBT dbdata; DBT tmpdb; XDR xdrs; int ret; OPENLOCK(db, KRB5_DB_LOCKMODE_EXCLUSIVE); if(entry->name == NULL) { ret = EINVAL; goto error; } dbkey.data = entry->name; dbkey.size = (strlen(entry->name) + 1); switch(db->db->get(db->db, &dbkey, &tmpdb, 0)) { case 0: break; case 1: ret = OSA_ADB_NOENT; goto error; default: ret = OSA_ADB_FAILURE; goto error; } xdralloc_create(&xdrs, XDR_ENCODE); if(!xdr_osa_policy_ent_rec(&xdrs, entry)) { xdr_destroy(&xdrs); ret = OSA_ADB_XDR_FAILURE; goto error; } dbdata.data = xdralloc_getdata(&xdrs); dbdata.size = xdr_getpos(&xdrs); switch(db->db->put(db->db, &dbkey, &dbdata, 0)) { case 0: if((db->db->sync(db->db, 0)) == -1) ret = OSA_ADB_FAILURE; ret = OSA_ADB_OK; break; default: ret = OSA_ADB_FAILURE; break; } xdr_destroy(&xdrs); error: CLOSELOCK(db); return ret; } /* * Function: osa_adb_iter_policy * * Purpose: iterate over the policy database. * * Arguments: * db (input) db handle * func (input) fucntion pointer to call * data opaque data type * 0 on success error code on failure * * Requires: * Effects: * Modifies: */ krb5_error_code osa_adb_iter_policy(osa_adb_policy_t db, osa_adb_iter_policy_func func, void *data) { DBT dbkey, dbdata; XDR xdrs; int ret; osa_policy_ent_t entry; char *aligned_data; OPENLOCK(db, KRB5_DB_LOCKMODE_EXCLUSIVE); /* hmmm */ if((ret = db->db->seq(db->db, &dbkey, &dbdata, R_FIRST)) == -1) { ret = errno; goto error; } while (ret == 0) { if (!(entry = (osa_policy_ent_t) malloc(sizeof(osa_policy_ent_rec)))) { ret = ENOMEM; goto error; } if(!(aligned_data = (char *) malloc(dbdata.size))) { ret = ENOMEM; goto error; } memcpy(aligned_data, dbdata.data, dbdata.size); memset(entry, 0, sizeof(osa_policy_ent_rec)); xdrmem_create(&xdrs, aligned_data, dbdata.size, XDR_DECODE); if(!xdr_osa_policy_ent_rec(&xdrs, entry)) { xdr_destroy(&xdrs); free(aligned_data); ret = OSA_ADB_FAILURE; goto error; } (*func)(data, entry); xdr_destroy(&xdrs); free(aligned_data); osa_free_policy_ent(entry); ret = db->db->seq(db->db, &dbkey, &dbdata, R_NEXT); } if(ret == -1) ret = errno; else ret = OSA_ADB_OK; error: CLOSELOCK(db); return ret; } void osa_free_policy_ent(osa_policy_ent_t val) { XDR xdrs; xdrmem_create(&xdrs, NULL, 0, XDR_FREE); xdr_osa_policy_ent_rec(&xdrs, val); free(val); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include ../Makefile.com include $(SRC)/lib/Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2006 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. */ /********************************************************************** * * C %name: db2_exp.c % * Instance: idc_sec_2 * Description: * %created_by: spradeep % * %date_created: Tue Apr 5 11:44:00 2005 % * **********************************************************************/ #ifndef lint static char *_csrc = "@(#) %filespec: db2_exp.c~5 % (%full_filespec: db2_exp.c~5:csrc:idc_sec#2 %)"; #endif #include "k5-int.h" #if HAVE_UNISTD_H #include #endif #include #include #include #include #include #include "kdb_db2.h" #include "kdb_xdr.h" #include "policy_db.h" /* Quick and dirty wrapper functions to provide for thread safety within the plugin, instead of making the kdb5 library do it. Eventually these should be integrated into the real functions. Some of the functions wrapped here are also called directly from within this library (e.g., create calls open), so simply dropping locking code into the top and bottom of each referenced function won't do. (We aren't doing recursive locks, currently.) */ static k5_mutex_t *krb5_db2_mutex; #define WRAP(NAME,TYPE,ARGLIST,ARGNAMES,ERROR_RESULT) \ static TYPE wrap_##NAME ARGLIST \ { \ TYPE result; \ int code = k5_mutex_lock (krb5_db2_mutex); \ if (code) { return ERROR_RESULT; } \ result = NAME ARGNAMES; \ k5_mutex_unlock (krb5_db2_mutex); \ return result; \ } \ /* hack: decl to allow a following ";" */ \ static TYPE wrap_##NAME () /* Two special cases: void (can't assign result), and krb5_error_code (return error from locking code). */ #define WRAP_VOID(NAME,ARGLIST,ARGNAMES) \ static void wrap_##NAME ARGLIST \ { \ int code = k5_mutex_lock (krb5_db2_mutex); \ if (code) { return; } \ NAME ARGNAMES; \ k5_mutex_unlock (krb5_db2_mutex); \ } \ /* hack: decl to allow a following ";" */ \ static void wrap_##NAME () #define WRAP_K(NAME,ARGLIST,ARGNAMES) \ WRAP(NAME,krb5_error_code,ARGLIST,ARGNAMES,code) WRAP_K (krb5_db2_open, ( krb5_context kcontext, char *conf_section, char **db_args, int mode ), (kcontext, conf_section, db_args, mode)); WRAP_K (krb5_db2_db_fini, (krb5_context ctx), (ctx)); WRAP_K (krb5_db2_create, ( krb5_context kcontext, char *conf_section, char **db_args ), (kcontext, conf_section, db_args)); WRAP_K (krb5_db2_destroy, ( krb5_context kcontext, char *conf_section, char **db_args ), (kcontext, conf_section, db_args)); WRAP_K (krb5_db2_db_get_age, (krb5_context ctx, char *s, time_t *t), (ctx, s, t)); WRAP_K (krb5_db2_db_set_option, ( krb5_context kcontext, int option, void *value ), (kcontext, option, value)); WRAP_K (krb5_db2_db_lock, ( krb5_context context, int in_mode), (context, in_mode)); WRAP_K (krb5_db2_db_unlock, (krb5_context ctx), (ctx)); WRAP_K (krb5_db2_db_get_principal, (krb5_context ctx, krb5_const_principal p, krb5_db_entry *d, int * i, krb5_boolean *b), (ctx, p, d, i, b)); WRAP_K (krb5_db2_db_free_principal, (krb5_context ctx, krb5_db_entry *d, int i), (ctx, d, i)); WRAP_K (krb5_db2_db_put_principal, (krb5_context ctx, krb5_db_entry *d, int *i, char **db_args), (ctx, d, i, db_args)); WRAP_K (krb5_db2_db_delete_principal, (krb5_context context, krb5_const_principal searchfor, int *nentries), (context, searchfor, nentries)); /* Solaris Kerberos: adding support for db_args */ WRAP_K (krb5_db2_db_iterate, (krb5_context ctx, char *s, krb5_error_code (*f) (krb5_pointer, krb5_db_entry *), krb5_pointer p, char **db_args), (ctx, s, f, p, db_args)); WRAP_K (krb5_db2_create_policy, (krb5_context context, osa_policy_ent_t entry), (context, entry)); WRAP_K (krb5_db2_get_policy, ( krb5_context kcontext, char *name, osa_policy_ent_t *policy, int *cnt), (kcontext, name, policy, cnt)); WRAP_K (krb5_db2_put_policy, ( krb5_context kcontext, osa_policy_ent_t policy ), (kcontext, policy)); WRAP_K (krb5_db2_iter_policy, ( krb5_context kcontext, char *match_entry, osa_adb_iter_policy_func func, void *data ), (kcontext, match_entry, func, data)); WRAP_K (krb5_db2_delete_policy, ( krb5_context kcontext, char *policy ), (kcontext, policy)); WRAP_VOID (krb5_db2_free_policy, ( krb5_context kcontext, osa_policy_ent_t entry ), (kcontext, entry)); WRAP (krb5_db2_alloc, void *, ( krb5_context kcontext, void *ptr, size_t size ), (kcontext, ptr, size), NULL); WRAP_VOID (krb5_db2_free, ( krb5_context kcontext, void *ptr ), (kcontext, ptr)); WRAP_K (krb5_db2_set_master_key_ext, ( krb5_context kcontext, char *pwd, krb5_keyblock *key), (kcontext, pwd, key)); WRAP_K (krb5_db2_db_get_mkey, ( krb5_context context, krb5_keyblock **key), (context, key)); WRAP_K (krb5_db2_promote_db, ( krb5_context kcontext, char *conf_section, char **db_args ), (kcontext, conf_section, db_args)); static krb5_error_code hack_init () { krb5_error_code c; c = krb5int_mutex_alloc (&krb5_db2_mutex); if (c) return c; return krb5_db2_lib_init (); } static krb5_error_code hack_cleanup (void) { krb5int_mutex_free (krb5_db2_mutex); krb5_db2_mutex = NULL; return krb5_db2_lib_cleanup(); } /* * Exposed API */ kdb_vftabl kdb_function_table = { /* major version number 1 */ 1, /* minor version number 0 */ 0, /* Solaris Kerberos: iprop support */ /* iprop_supported, yes for db2 */ 1, /* init_library */ hack_init, /* fini_library */ hack_cleanup, /* init_module */ wrap_krb5_db2_open, /* fini_module */ wrap_krb5_db2_db_fini, /* db_create */ wrap_krb5_db2_create, /* db_destroy */ wrap_krb5_db2_destroy, /* db_get_age */ wrap_krb5_db2_db_get_age, /* db_set_option */ wrap_krb5_db2_db_set_option, /* db_lock */ wrap_krb5_db2_db_lock, /* db_unlock */ wrap_krb5_db2_db_unlock, /* db_get_principal */ wrap_krb5_db2_db_get_principal, /* Solaris Kerberos: need a nolock for iprop */ /* db_get_principal_nolock */ krb5_db2_db_get_principal, /* db_free_principal */ wrap_krb5_db2_db_free_principal, /* db_put_principal */ wrap_krb5_db2_db_put_principal, /* db_delete_principal */ wrap_krb5_db2_db_delete_principal, /* db_iterate */ wrap_krb5_db2_db_iterate, /* db_create_policy */ wrap_krb5_db2_create_policy, /* db_get_policy */ wrap_krb5_db2_get_policy, /* db_put_policy */ wrap_krb5_db2_put_policy, /* db_iter_policy */ wrap_krb5_db2_iter_policy, /* db_delete_policy */ wrap_krb5_db2_delete_policy, /* db_free_policy */ wrap_krb5_db2_free_policy, /* db_supported_realms */ NULL, /* db_free_supported_realms */ NULL, /* errcode_2_string */ krb5_db2_errcode_2_string, /* release_errcode_string */ krb5_db2_release_errcode_string, /* db_alloc */ wrap_krb5_db2_alloc, /* db_free */ wrap_krb5_db2_free, /* set_master_key */ wrap_krb5_db2_set_master_key_ext, /* get_master_key */ wrap_krb5_db2_db_get_mkey, /* blah blah blah */ 0,0,0,0,0,0, /* promote_db */ wrap_krb5_db2_promote_db, }; /* * Copyright 1997-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KRB5_KDB_KDB_COMPAT_H #define _KRB5_KDB_KDB_COMPAT_H #ifdef __cplusplus extern "C" { #endif /* * lib/kdb/kdb_compat.h * * Copyright 1994 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * * KDC Database interface definitions. */ typedef struct _old_krb5_encrypted_keyblock { krb5_enctype enctype; int length; krb5_octet *contents; } old_krb5_encrypted_keyblock; typedef struct old_krb5_principal_data { krb5_magic magic; krb5_data realm; krb5_data *data; /* An array of strings */ krb5_int32 length; krb5_int32 type; } old_krb5_principal_data; typedef old_krb5_principal_data *old_krb5_principal; /* * Note --- this structure cannot be modified without changing the * database version number in libkdb.a */ typedef struct _old_krb5_db_entry { old_krb5_principal principal; old_krb5_encrypted_keyblock key; krb5_kvno kvno; krb5_deltat max_life; krb5_deltat max_renewable_life; krb5_kvno mkvno; /* master encryption key vno */ krb5_timestamp expiration; /* This is when the client expires */ krb5_timestamp pw_expiration; /* This is when its password does */ krb5_timestamp last_pwd_change; /* Last time of password change */ krb5_timestamp last_success; /* Last successful password */ krb5_timestamp last_failed; /* Last failed password attempt */ krb5_kvno fail_auth_count; /* # of failed password attempts */ old_krb5_principal mod_name; krb5_timestamp mod_date; krb5_flags attributes; krb5_int32 salt_type:8, salt_length:24; krb5_octet *salt; old_krb5_encrypted_keyblock alt_key; krb5_int32 alt_salt_type:8, alt_salt_length:24; krb5_octet *alt_salt; krb5_int32 expansion[8]; } old_krb5_db_entry; #ifdef __cplusplus } #endif #endif /* !_KRB5_KDB_KDB_COMPAT_H */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * lib/kdb/kdb_db2.c * * Copyright 1997,2006 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ /* * Copyright (C) 1998 by the FundsXpress, INC. * * All rights reserved. * * Export of this software from the United States of America may require * a specific license from the United States Government. It is the * responsibility of any person or organization contemplating export to * obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. FundsXpress makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "k5-int.h" #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #include "kdb5.h" #include "kdb_db2.h" #include "kdb_xdr.h" #include "policy_db.h" #include #define KDB_DB2_DATABASE_NAME "database_name" #include "kdb_db2.h" static char *gen_dbsuffix(char *, char *); static krb5_error_code krb5_db2_db_start_update(krb5_context); static krb5_error_code krb5_db2_db_end_update(krb5_context); static krb5_error_code krb5_db2_db_set_name(krb5_context, char *, int); krb5_error_code krb5_db2_db_lock(krb5_context, int); static krb5_error_code krb5_db2_db_set_hashfirst(krb5_context, int); /* * Solaris Kerberos * Extra error handling */ char errbuf[1024]; static void krb5_db2_prepend_err_str(krb5_context , const char *, krb5_error_code, krb5_error_code); static char default_db_name[] = DEFAULT_KDB_FILE; /* * Locking: * * There are two distinct locking protocols used. One is designed to * lock against processes (the admin_server, for one) which make * incremental changes to the database; the other is designed to lock * against utilities (kdb5_edit, kpropd, kdb5_convert) which replace the * entire database in one fell swoop. * * The first locking protocol is implemented using flock() in the * krb_dbl_lock() and krb_dbl_unlock routines. * * The second locking protocol is necessary because DBM "files" are * actually implemented as two separate files, and it is impossible to * atomically rename two files simultaneously. It assumes that the * database is replaced only very infrequently in comparison to the time * needed to do a database read operation. * * A third file is used as a "version" semaphore; the modification * time of this file is the "version number" of the database. * At the start of a read operation, the reader checks the version * number; at the end of the read operation, it checks again. If the * version number changed, or if the semaphore was nonexistant at * either time, the reader sleeps for a second to let things * stabilize, and then tries again; if it does not succeed after * KRB5_DBM_MAX_RETRY attempts, it gives up. * * On update, the semaphore file is deleted (if it exists) before any * update takes place; at the end of the update, it is replaced, with * a version number strictly greater than the version number which * existed at the start of the update. * * If the system crashes in the middle of an update, the semaphore * file is not automatically created on reboot; this is a feature, not * a bug, since the database may be inconsistant. Note that the * absence of a semaphore file does not prevent another _update_ from * taking place later. Database replacements take place automatically * only on slave servers; a crash in the middle of an update will be * fixed by the next slave propagation. A crash in the middle of an * update on the master would be somewhat more serious, but this would * likely be noticed by an administrator, who could fix the problem and * retry the operation. */ #define free_dbsuffix(name) free(name) /* * Routines to deal with context. */ #define k5db2_inited(c) (c && c->db_context \ && ((kdb5_dal_handle*)c->db_context)->db_context \ && ((krb5_db2_context *) ((kdb5_dal_handle*)c->db_context)->db_context)->db_inited) static krb5_error_code krb5_db2_get_db_opt(char *input, char **opt, char **val) { char *pos = strchr(input, '='); if (pos == NULL) { *opt = NULL; *val = strdup(input); if (*val == NULL) { return ENOMEM; } } else { *opt = malloc((pos - input) + 1); *val = strdup(pos + 1); if (!*opt || !*val) { return ENOMEM; } memcpy(*opt, input, pos - input); (*opt)[pos - input] = '\0'; } return (0); } /* * Restore the default context. */ static void k5db2_clear_context(krb5_db2_context *dbctx) { /* * Free any dynamically allocated memory. File descriptors and locks * are the caller's problem. */ if (dbctx->db_lf_name) free(dbctx->db_lf_name); if (dbctx->db_name && (dbctx->db_name != default_db_name)) free(dbctx->db_name); /* * Clear the structure and reset the defaults. */ memset((char *) dbctx, 0, sizeof(krb5_db2_context)); dbctx->db_name = default_db_name; dbctx->db_nb_locks = FALSE; dbctx->tempdb = FALSE; } static krb5_error_code k5db2_init_context(krb5_context context) { krb5_db2_context *db_ctx; kdb5_dal_handle *dal_handle; dal_handle = (kdb5_dal_handle *) context->db_context; if (dal_handle->db_context == NULL) { db_ctx = (krb5_db2_context *) malloc(sizeof(krb5_db2_context)); if (db_ctx == NULL) return ENOMEM; else { memset((char *) db_ctx, 0, sizeof(krb5_db2_context)); k5db2_clear_context((krb5_db2_context *) db_ctx); dal_handle->db_context = (void *) db_ctx; } } return (0); } /* * Utility routine: generate name of database file. */ static char * gen_dbsuffix(char *db_name, char *sfx) { char *dbsuffix; if (sfx == NULL) return ((char *) NULL); dbsuffix = malloc(strlen(db_name) + strlen(sfx) + 1); if (!dbsuffix) return (0); /*LINTED*/ (void) strcpy(dbsuffix, db_name); /*LINTED*/ (void) strcat(dbsuffix, sfx); return dbsuffix; } static DB * k5db2_dbopen(krb5_db2_context *dbc, char *fname, int flags, int mode, int tempdb) { DB *db; BTREEINFO bti; HASHINFO hashi; bti.flags = 0; bti.cachesize = 0; bti.psize = 4096; bti.lorder = 0; bti.minkeypage = 0; bti.compare = NULL; bti.prefix = NULL; if (tempdb) { fname = gen_dbsuffix(fname, "~"); } else { fname = strdup(fname); } if (fname == NULL) { errno = ENOMEM; return NULL; } hashi.bsize = 4096; hashi.cachesize = 0; hashi.ffactor = 40; hashi.hash = NULL; hashi.lorder = 0; hashi.nelem = 1; db = dbopen(fname, flags, mode, dbc->hashfirst ? DB_HASH : DB_BTREE, dbc->hashfirst ? (void *) &hashi : (void *) &bti); if (db != NULL) { free(fname); return db; } switch (errno) { #ifdef EFTYPE case EFTYPE: #endif case EINVAL: db = dbopen(fname, flags, mode, dbc->hashfirst ? DB_BTREE : DB_HASH, dbc->hashfirst ? (void *) &bti : (void *) &hashi); if (db != NULL) dbc->hashfirst = !dbc->hashfirst; /* FALLTHROUGH */ default: free(fname); return db; } } static krb5_error_code krb5_db2_db_set_hashfirst(krb5_context context, int hashfirst) { krb5_db2_context *dbc; kdb5_dal_handle *dal_handle; if (k5db2_inited(context)) return KRB5_KDB_DBNOTINITED; dal_handle = (kdb5_dal_handle *) context->db_context; dbc = (krb5_db2_context *) dal_handle->db_context; dbc->hashfirst = hashfirst; return 0; } /* * initialization for data base routines. */ krb5_error_code krb5_db2_db_init(krb5_context context) { char *filename = NULL; krb5_db2_context *db_ctx; krb5_error_code retval; kdb5_dal_handle *dal_handle; char policy_db_name[1024], policy_lock_name[1024]; if (k5db2_inited(context)) return 0; /* Check for presence of our context, if not present, allocate one. */ if ((retval = k5db2_init_context(context))) return (retval); dal_handle = (kdb5_dal_handle *) context->db_context; db_ctx = dal_handle->db_context; db_ctx->db = NULL; if (!(filename = gen_dbsuffix(db_ctx->db_name, db_ctx->tempdb ?KDB2_TEMP_LOCK_EXT:KDB2_LOCK_EXT))) return ENOMEM; db_ctx->db_lf_name = filename; /* so it gets freed by clear_context */ /* * should be opened read/write so that write locking can work with * POSIX systems */ if ((db_ctx->db_lf_file = open(filename, O_RDWR, 0666)) < 0) { if ((db_ctx->db_lf_file = open(filename, O_RDONLY, 0666)) < 0) { retval = errno; /* Solaris Kerberos: Better error logging */ (void) snprintf(errbuf, sizeof(errbuf), gettext("Failed to open \"%s\": "), filename); krb5_db2_prepend_err_str(context, errbuf, retval, retval); goto err_out; } } db_ctx->db_inited++; if ((retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time))) goto err_out; sprintf(policy_db_name, db_ctx->tempdb ? "%s~.kadm5" : "%s.kadm5", db_ctx->db_name); sprintf(policy_lock_name, "%s.lock", policy_db_name); if ((retval = osa_adb_init_db(&db_ctx->policy_db, policy_db_name, policy_lock_name, OSA_ADB_POLICY_DB_MAGIC))) { /* Solaris Kerberos: Better error logging */ snprintf(errbuf, sizeof(errbuf), gettext("Failed to initialize db, \"%s\", lockfile, \"%s\" : "), policy_db_name, policy_lock_name); krb5_db2_prepend_err_str(context, errbuf, retval, retval); goto err_out; } return 0; err_out: db_ctx->db = NULL; k5db2_clear_context(db_ctx); return (retval); } /* * gracefully shut down database--must be called by ANY program that does * a krb5_db2_db_init */ krb5_error_code krb5_db2_db_fini(krb5_context context) { krb5_error_code retval = 0; krb5_db2_context *db_ctx; kdb5_dal_handle *dal_handle; dal_handle = (kdb5_dal_handle *) context->db_context; if (dal_handle == NULL) { return 0; } db_ctx = (krb5_db2_context *) dal_handle->db_context; if (k5db2_inited(context)) { if (close(db_ctx->db_lf_file)) retval = errno; else retval = 0; } if (db_ctx) { if (db_ctx->policy_db) { retval = osa_adb_fini_db(db_ctx->policy_db, OSA_ADB_POLICY_DB_MAGIC); if (retval) return retval; } k5db2_clear_context(db_ctx); /* free(dal_handle->db_context); */ dal_handle->db_context = NULL; } return retval; } /* * Set/Get the master key associated with the database */ krb5_error_code krb5_db2_db_set_mkey(krb5_context context, krb5_keyblock *key) { krb5_db2_context *db_ctx; kdb5_dal_handle *dal_handle; if (!k5db2_inited(context)) return (KRB5_KDB_DBNOTINITED); dal_handle = (kdb5_dal_handle *) context->db_context; db_ctx = dal_handle->db_context; db_ctx->db_master_key = key; return 0; } krb5_error_code krb5_db2_db_get_mkey(krb5_context context, krb5_keyblock **key) { krb5_db2_context *db_ctx; kdb5_dal_handle *dal_handle; if (!k5db2_inited(context)) return (KRB5_KDB_DBNOTINITED); dal_handle = (kdb5_dal_handle *) context->db_context; db_ctx = dal_handle->db_context; *key = db_ctx->db_master_key; return 0; } /* * Set the "name" of the current database to some alternate value. * * Passing a null pointer as "name" will set back to the default. * If the alternate database doesn't exist, nothing is changed. * * XXX rethink this */ static krb5_error_code krb5_db2_db_set_name(krb5_context context, char *name, int tempdb) { DB *db; krb5_db2_context *db_ctx; krb5_error_code kret; kdb5_dal_handle *dal_handle; if (k5db2_inited(context)) return KRB5_KDB_DBINITED; /* Check for presence of our context, if not present, allocate one. */ if ((kret = k5db2_init_context(context))) return (kret); if (name == NULL) name = default_db_name; dal_handle = (kdb5_dal_handle *) context->db_context; db_ctx = dal_handle->db_context; db_ctx->tempdb = tempdb; db = k5db2_dbopen(db_ctx, name, O_RDONLY, 0, tempdb); if (db == NULL) return errno; db_ctx->db_name = strdup(name); if (db_ctx->db_name == NULL) { (*db->close) (db); return ENOMEM; } (*db->close) (db); return 0; } /* * Return the last modification time of the database. * * Think about using fstat. */ krb5_error_code krb5_db2_db_get_age(krb5_context context, char *db_name, time_t *age) { krb5_db2_context *db_ctx; kdb5_dal_handle *dal_handle; struct stat st; if (!k5db2_inited(context)) return (KRB5_KDB_DBNOTINITED); dal_handle = (kdb5_dal_handle *) context->db_context; db_ctx = (krb5_db2_context *) dal_handle->db_context; if (fstat(db_ctx->db_lf_file, &st) < 0) *age = -1; else *age = st.st_mtime; return 0; } /* * Remove the semaphore file; indicates that database is currently * under renovation. * * This is only for use when moving the database out from underneath * the server (for example, during slave updates). */ static krb5_error_code krb5_db2_db_start_update(krb5_context context) { return 0; } static krb5_error_code krb5_db2_db_end_update(krb5_context context) { krb5_error_code retval; krb5_db2_context *db_ctx; kdb5_dal_handle *dal_handle; struct stat st; time_t now; struct utimbuf utbuf; if (!k5db2_inited(context)) return (KRB5_KDB_DBNOTINITED); retval = 0; dal_handle = (kdb5_dal_handle *) context->db_context; db_ctx = dal_handle->db_context; now = time((time_t *) NULL); if (fstat(db_ctx->db_lf_file, &st) == 0) { if (st.st_mtime >= now) { utbuf.actime = st.st_mtime + 1; utbuf.modtime = st.st_mtime + 1; if (utime(db_ctx->db_lf_name, &utbuf)) retval = errno; } else { if (utime(db_ctx->db_lf_name, (struct utimbuf *) NULL)) retval = errno; } if (retval) { /* Solaris Kerberos: Better error logging */ snprintf(errbuf, sizeof(errbuf), gettext("Failed to modify " "access and modification times for \"%s\": "), db_ctx->db_lf_name); krb5_db2_prepend_err_str(context, errbuf, retval, retval); } } else { retval = errno; /* Solaris Kerberos: Better error logging */ snprintf(errbuf, sizeof(errbuf), gettext("Failed to stat \"%s\": "), db_ctx->db_lf_name); krb5_db2_prepend_err_str(context, errbuf, retval, retval); } if (!retval) { if (fstat(db_ctx->db_lf_file, &st) == 0) db_ctx->db_lf_time = st.st_mtime; else { retval = errno; /* Solaris Kerberos: Better error logging */ snprintf(errbuf, sizeof(errbuf), gettext("Failed to stat \"%s\": "), db_ctx->db_lf_name); krb5_db2_prepend_err_str(context, errbuf, retval, retval); } } return (retval); } #define MAX_LOCK_TRIES 5 krb5_error_code krb5_db2_db_lock(krb5_context context, int in_mode) { krb5_db2_context *db_ctx; int krb5_lock_mode; DB *db; krb5_error_code retval; time_t mod_time; kdb5_dal_handle *dal_handle; int mode, gotlock, tries; switch (in_mode) { case KRB5_DB_LOCKMODE_PERMANENT: mode = KRB5_DB_LOCKMODE_EXCLUSIVE; break; case KRB5_DB_LOCKMODE_EXCLUSIVE: mode = KRB5_LOCKMODE_EXCLUSIVE; break; case KRB5_DB_LOCKMODE_SHARED: mode = KRB5_LOCKMODE_SHARED; break; default: return EINVAL; } if (!k5db2_inited(context)) return KRB5_KDB_DBNOTINITED; dal_handle = (kdb5_dal_handle *) context->db_context; db_ctx = (krb5_db2_context *) dal_handle->db_context; if (db_ctx->db_locks_held && (db_ctx->db_lock_mode >= mode)) { /* No need to upgrade lock, just return */ db_ctx->db_locks_held++; goto policy_lock; } if ((mode != KRB5_LOCKMODE_SHARED) && (mode != KRB5_LOCKMODE_EXCLUSIVE)) return KRB5_KDB_BADLOCKMODE; krb5_lock_mode = mode | KRB5_LOCKMODE_DONTBLOCK; for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) { retval = krb5_lock_file(context, db_ctx->db_lf_file, krb5_lock_mode); if (retval == 0) { gotlock++; break; } else if (retval == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE) { /* tried to exclusive-lock something we don't have */ /* write access to */ /* Solaris Kerberos: Better error logging */ snprintf(errbuf, sizeof(errbuf), gettext("Failed to exclusively lock \"%s\": "), db_ctx->db_lf_name); krb5_db2_prepend_err_str(context, errbuf, EBADF, EBADF); return KRB5_KDB_CANTLOCK_DB; } sleep(1); } if (retval) { /* Solaris Kerberos: Better error logging */ snprintf(errbuf, sizeof(errbuf), gettext("Failed to lock \"%s\": "), db_ctx->db_lf_name); krb5_db2_prepend_err_str(context, errbuf, retval, retval); } if (retval == EACCES) return KRB5_KDB_CANTLOCK_DB; else if (retval == EAGAIN || retval == EWOULDBLOCK) return OSA_ADB_CANTLOCK_DB; else if (retval != 0) return retval; if ((retval = krb5_db2_db_get_age(context, NULL, &mod_time))) goto lock_error; db = k5db2_dbopen(db_ctx, db_ctx->db_name, mode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR, 0600, db_ctx->tempdb); if (db) { db_ctx->db_lf_time = mod_time; db_ctx->db = db; } else { retval = errno; /* Solaris Kerberos: Better error logging */ snprintf(errbuf, sizeof(errbuf), gettext("Failed to open db \"%s\": "), db_ctx->db_name); krb5_db2_prepend_err_str(context, errbuf, retval, retval); db_ctx->db = NULL; goto lock_error; } db_ctx->db_lock_mode = mode; db_ctx->db_locks_held++; policy_lock: if ((retval = osa_adb_get_lock(db_ctx->policy_db, in_mode))) { krb5_db2_db_unlock(context); } return retval; lock_error:; db_ctx->db_lock_mode = 0; db_ctx->db_locks_held = 0; krb5_db2_db_unlock(context); return retval; } krb5_error_code krb5_db2_db_unlock(krb5_context context) { krb5_db2_context *db_ctx; kdb5_dal_handle *dal_handle; DB *db; krb5_error_code retval; if (!k5db2_inited(context)) return KRB5_KDB_DBNOTINITED; dal_handle = (kdb5_dal_handle *) context->db_context; db_ctx = (krb5_db2_context *) dal_handle->db_context; if ((retval = osa_adb_release_lock(db_ctx->policy_db))) { return retval; } if (!db_ctx->db_locks_held) /* lock already unlocked */ return KRB5_KDB_NOTLOCKED; db = db_ctx->db; if (--(db_ctx->db_locks_held) == 0) { (*db->close) (db); db_ctx->db = NULL; retval = krb5_lock_file(context, db_ctx->db_lf_file, KRB5_LOCKMODE_UNLOCK); db_ctx->db_lock_mode = 0; return (retval); } return 0; } /* * Create the database, assuming it's not there. */ krb5_error_code krb5_db2_db_create(krb5_context context, char *db_name, krb5_int32 flags) { register krb5_error_code retval = 0; kdb5_dal_handle *dal_handle; char *okname; char *db_name2 = NULL; int fd; krb5_db2_context *db_ctx; DB *db; char policy_db_name[1024], policy_lock_name[1024]; if ((retval = k5db2_init_context(context))) return (retval); dal_handle = (kdb5_dal_handle *) context->db_context; db_ctx = (krb5_db2_context *) dal_handle->db_context; switch (flags) { case KRB5_KDB_CREATE_HASH: if ((retval = krb5_db2_db_set_hashfirst(context, TRUE))) return retval; break; case KRB5_KDB_CREATE_BTREE: case 0: if ((retval = krb5_db2_db_set_hashfirst(context, FALSE))) return retval; break; default: return KRB5_KDB_BAD_CREATEFLAGS; } db = k5db2_dbopen(db_ctx, db_name, O_RDWR | O_CREAT | O_EXCL, 0600, db_ctx->tempdb); if (db == NULL) { retval = errno; /* Solaris Kerberos: Better error logging */ snprintf(errbuf, sizeof(errbuf), gettext("Failed to open \"%s\": "), db_name); krb5_db2_prepend_err_str(context, errbuf, retval, retval); } else (*db->close) (db); if (retval == 0) { db_name2 = db_ctx->tempdb ? gen_dbsuffix(db_name, "~") : strdup(db_name); if (db_name2 == NULL) return ENOMEM; okname = gen_dbsuffix(db_name2, KDB2_LOCK_EXT); if (!okname) retval = ENOMEM; else { fd = open(okname, O_CREAT | O_RDWR | O_TRUNC, 0600); if (fd < 0) { retval = errno; /* Solaris Kerberos: Better error logging */ snprintf(errbuf, sizeof(errbuf), gettext("Failed to open \"%s\": "), okname); krb5_db2_prepend_err_str(context, errbuf, retval, retval); } else close(fd); free_dbsuffix(okname); } } sprintf(policy_db_name, "%s.kadm5", db_name2); sprintf(policy_lock_name, "%s.lock", policy_db_name); retval = osa_adb_create_db(policy_db_name, policy_lock_name, OSA_ADB_POLICY_DB_MAGIC); free(db_name2); return retval; } /* * Destroy the database. Zero's out all of the files, just to be sure. */ static krb5_error_code destroy_file_suffix(char *dbname, char *suffix) { char *filename; struct stat statb; int nb, fd; unsigned int j; off_t pos; char buf[BUFSIZ]; char zbuf[BUFSIZ]; int dowrite; filename = gen_dbsuffix(dbname, suffix); if (filename == 0) return ENOMEM; if ((fd = open(filename, O_RDWR, 0)) < 0) { free(filename); return errno; } /* fstat() will probably not fail unless using a remote filesystem * (which is inappropriate for the kerberos database) so this check * is mostly paranoia. */ if (fstat(fd, &statb) == -1) { int retval = errno; free(filename); return retval; } /* * Stroll through the file, reading in BUFSIZ chunks. If everything * is zero, then we're done for that block, otherwise, zero the block. * We would like to just blast through everything, but some DB * implementations make holey files and writing data to the holes * causes actual blocks to be allocated which is no good, since * we're just about to unlink it anyways. */ memset(zbuf, 0, BUFSIZ); pos = 0; while (pos < statb.st_size) { dowrite = 0; nb = read(fd, buf, BUFSIZ); if (nb < 0) { int retval = errno; free(filename); return retval; } for (j = 0; j < nb; j++) { if (buf[j] != '\0') { dowrite = 1; break; } } /* For signedness */ j = nb; if (dowrite) { lseek(fd, pos, SEEK_SET); nb = write(fd, zbuf, j); if (nb < 0) { int retval = errno; free(filename); return retval; } } pos += nb; } /* ??? Is fsync really needed? I don't know of any non-networked * filesystem which will discard queued writes to disk if a file * is deleted after it is closed. --jfc */ #ifndef NOFSYNC fsync(fd); #endif close(fd); if (unlink(filename)) { free(filename); return (errno); } free(filename); return (0); } /* * Since the destroy operation happens outside the init/fini bracket, we * have some tomfoolery to undergo here. If we're operating under no * database context, then we initialize with the default. If the caller * wishes a different context (e.g. different dispatch table), it's their * responsibility to call kdb5_db_set_dbops() before this call. That will * set up the right dispatch table values (e.g. name extensions). * * Not quite valid due to ripping out of dbops... */ krb5_error_code krb5_db2_db_destroy(krb5_context context, char *dbname) { krb5_error_code retval1, retval2; krb5_boolean tmpcontext; char policy_db_name[1024], policy_lock_name[1024]; tmpcontext = 0; if (!context->db_context || !((kdb5_dal_handle *) context->db_context)->db_context) { tmpcontext = 1; if ((retval1 = k5db2_init_context(context))) return (retval1); } retval1 = retval2 = 0; retval1 = destroy_file_suffix(dbname, ""); retval2 = destroy_file_suffix(dbname, KDB2_LOCK_EXT); if (tmpcontext) { k5db2_clear_context((krb5_db2_context *) ((kdb5_dal_handle *) context-> db_context)->db_context); free(((kdb5_dal_handle *) context->db_context)->db_context); ((kdb5_dal_handle *) context->db_context)->db_context = NULL; } if (retval1 || retval2) return (retval1 ? retval1 : retval2); assert (strlen(dbname) + strlen("%s.kadm5") < sizeof(policy_db_name)); sprintf(policy_db_name, "%s.kadm5", dbname); /* XXX finish this */ sprintf(policy_lock_name, "%s.lock", policy_db_name); retval1 = osa_adb_destroy_db(policy_db_name, policy_lock_name, OSA_ADB_POLICY_DB_MAGIC); return retval1; } /* * look up a principal in the data base. * returns number of entries found, and whether there were * more than requested. */ krb5_error_code krb5_db2_db_get_principal(krb5_context context, krb5_const_principal searchfor, krb5_db_entry *entries, /* filled in */ int *nentries, /* how much room/how many found */ krb5_boolean *more) /* are there more? */ { krb5_db2_context *db_ctx; krb5_error_code retval; DB *db; DBT key, contents; krb5_data keydata, contdata; int trynum, dbret; kdb5_dal_handle *dal_handle; *more = FALSE; *nentries = 0; if (!k5db2_inited(context)) return KRB5_KDB_DBNOTINITED; dal_handle = (kdb5_dal_handle *) context->db_context; db_ctx = (krb5_db2_context *) dal_handle->db_context; for (trynum = 0; trynum < KRB5_DB2_MAX_RETRY; trynum++) { if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED))) { if (db_ctx->db_nb_locks) return (retval); sleep(1); continue; } break; } if (trynum == KRB5_DB2_MAX_RETRY) return KRB5_KDB_DB_INUSE; /* XXX deal with wildcard lookups */ retval = krb5_encode_princ_dbkey(context, &keydata, searchfor); if (retval) goto cleanup; key.data = keydata.data; key.size = keydata.length; db = db_ctx->db; dbret = (*db->get) (db, &key, &contents, 0); retval = errno; krb5_free_data_contents(context, &keydata); switch (dbret) { case 1: retval = 0; /* FALLTHROUGH */ case -1: default: *nentries = 0; goto cleanup; case 0: contdata.data = contents.data; contdata.length = contents.size; retval = krb5_decode_princ_contents(context, &contdata, entries); if (!retval) *nentries = 1; break; } cleanup: (void) krb5_db2_db_unlock(context); /* unlock read lock */ return retval; } /* Free stuff returned by krb5_db2_db_get_principal. */ krb5_error_code krb5_db2_db_free_principal(krb5_context context, krb5_db_entry *entries, int nentries) { register int i; for (i = 0; i < nentries; i++) krb5_dbe_free_contents(context, &entries[i]); return 0; } /* Stores the *"nentries" entry structures pointed to by "entries" in the database. *"nentries" is updated upon return to reflect the number of records acutally stored; the first *"nstored" records will have been stored in the database (even if an error occurs). */ krb5_error_code krb5_db2_db_put_principal(krb5_context context, krb5_db_entry *entries, int *nentries, /* number of entry structs to update */ char **db_args) { int i, n, dbret; DB *db; DBT key, contents; krb5_data contdata, keydata; krb5_error_code retval; krb5_db2_context *db_ctx; kdb5_dal_handle *dal_handle; kdb_incr_update_t *upd, *fupd; char *princ_name = NULL; kdb_log_context *log_ctx; krb5_clear_error_message (context); if (db_args) { /* DB2 does not support db_args DB arguments for principal */ krb5_set_error_message(context, EINVAL, gettext("Unsupported argument \"%s\" for db2"), db_args[0]); return EINVAL; } log_ctx = context->kdblog_context; n = *nentries; *nentries = 0; if (!k5db2_inited(context)) return KRB5_KDB_DBNOTINITED; dal_handle = (kdb5_dal_handle *) context->db_context; db_ctx = (krb5_db2_context *) dal_handle->db_context; if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) return retval; /* * Solaris Kerberos: We need the lock since ulog_conv_2logentry() does a get */ if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { if (!(upd = (kdb_incr_update_t *) malloc(sizeof (kdb_incr_update_t)*n))) { retval = errno; goto err_lock; } fupd = upd; (void) memset(upd, 0, sizeof(kdb_incr_update_t)*n); if ((retval = ulog_conv_2logentry(context, entries, upd, n))) { goto err_lock; } } db = db_ctx->db; if ((retval = krb5_db2_db_start_update(context))) { (void) krb5_db2_db_unlock(context); goto err_lock; } /* for each one, stuff temps, and do replace/append */ for (i = 0; i < n; i++) { /* * Solaris Kerberos: We'll be sharing the same locks as db for logging */ if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { if ((retval = krb5_unparse_name(context, entries->princ, &princ_name))) goto err_lock; upd->kdb_princ_name.utf8str_t_val = princ_name; upd->kdb_princ_name.utf8str_t_len = strlen(princ_name); if (retval = ulog_add_update(context, upd)) goto err_lock; } retval = krb5_encode_princ_contents(context, &contdata, entries); if (retval) break; contents.data = contdata.data; contents.size = contdata.length; retval = krb5_encode_princ_dbkey(context, &keydata, entries->princ); if (retval) { krb5_free_data_contents(context, &contdata); break; } key.data = keydata.data; key.size = keydata.length; dbret = (*db->put) (db, &key, &contents, 0); retval = dbret ? errno : 0; krb5_free_data_contents(context, &keydata); krb5_free_data_contents(context, &contdata); if (retval) break; else if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { /* * We need to make sure the db record is synced before we mark * it as committed via finish_update. */ dbret = (*db->sync)(db, 0); if (dbret) { retval = errno; goto err_lock; } (void) ulog_finish_update(context, upd); upd++; } entries++; /* bump to next struct */ } (void) krb5_db2_db_end_update(context); err_lock: (void) krb5_db2_db_unlock(context); /* unlock database */ if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) ulog_free_entries(fupd, n); *nentries = i; return (retval); } /* * delete a principal from the data base. * returns number of entries removed */ krb5_error_code krb5_db2_db_delete_principal(krb5_context context, krb5_const_principal searchfor, int *nentries) /* how many found & deleted */ { krb5_error_code retval; krb5_db_entry entry; krb5_db2_context *db_ctx; DB *db; DBT key, contents; krb5_data keydata, contdata; int i, dbret; kdb5_dal_handle *dal_handle; kdb_incr_update_t upd; char *princ_name = NULL; kdb_log_context *log_ctx; log_ctx = context->kdblog_context; if (!k5db2_inited(context)) return KRB5_KDB_DBNOTINITED; dal_handle = (kdb5_dal_handle *) context->db_context; db_ctx = (krb5_db2_context *) dal_handle->db_context; if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) return (retval); if ((retval = krb5_db2_db_start_update(context))) { (void) krb5_db2_db_unlock(context); /* unlock write lock */ return (retval); } if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor))) goto cleanup; key.data = keydata.data; key.size = keydata.length; db = db_ctx->db; dbret = (*db->get) (db, &key, &contents, 0); retval = errno; switch (dbret) { case 1: retval = KRB5_KDB_NOENTRY; /* FALLTHROUGH */ case -1: default: *nentries = 0; goto cleankey; case 0: ; } /* * Solaris Kerberos: We'll be sharing the same locks as db for logging */ if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { if ((retval = krb5_unparse_name(context, searchfor, &princ_name))) { (void) krb5_db2_db_unlock(context); return retval; } (void) memset(&upd, 0, sizeof (kdb_incr_update_t)); upd.kdb_princ_name.utf8str_t_val = princ_name; upd.kdb_princ_name.utf8str_t_len = strlen(princ_name); if (retval = ulog_delete_update(context, &upd)) { free(princ_name); (void) krb5_db2_db_unlock(context); return retval; } free(princ_name); } memset((char *) &entry, 0, sizeof(entry)); contdata.data = contents.data; contdata.length = contents.size; retval = krb5_decode_princ_contents(context, &contdata, &entry); if (retval) goto cleankey; *nentries = 1; /* Clear encrypted key contents */ for (i = 0; i < entry.n_key_data; i++) { if (entry.key_data[i].key_data_length[0]) { memset((char *) entry.key_data[i].key_data_contents[0], 0, (unsigned) entry.key_data[i].key_data_length[0]); } } retval = krb5_encode_princ_contents(context, &contdata, &entry); krb5_dbe_free_contents(context, &entry); if (retval) goto cleankey; contents.data = contdata.data; contents.size = contdata.length; dbret = (*db->put) (db, &key, &contents, 0); retval = dbret ? errno : 0; krb5_free_data_contents(context, &contdata); if (retval) goto cleankey; dbret = (*db->del) (db, &key, 0); retval = dbret ? errno : 0; /* * We need to commit our update upon success */ if (!retval) if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) (void) ulog_finish_update(context, &upd); cleankey: krb5_free_data_contents(context, &keydata); cleanup: (void) krb5_db2_db_end_update(context); (void) krb5_db2_db_unlock(context); /* unlock write lock */ return retval; } krb5_error_code krb5_db2_db_iterate_ext(krb5_context context, krb5_error_code(*func) (krb5_pointer, krb5_db_entry *), krb5_pointer func_arg, int backwards, int recursive) { krb5_db2_context *db_ctx; DB *db; DBT key, contents; krb5_data contdata; krb5_db_entry entries; krb5_error_code retval; kdb5_dal_handle *dal_handle; int dbret; void *cookie; cookie = NULL; if (!k5db2_inited(context)) return KRB5_KDB_DBNOTINITED; dal_handle = (kdb5_dal_handle *) context->db_context; db_ctx = (krb5_db2_context *) dal_handle->db_context; retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED); if (retval) return retval; db = db_ctx->db; if (recursive && db->type != DB_BTREE) { (void) krb5_db2_db_unlock(context); return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ } if (!recursive) { dbret = (*db->seq) (db, &key, &contents, backwards ? R_LAST : R_FIRST); } else { #ifdef HAVE_BT_RSEQ dbret = bt_rseq(db, &key, &contents, &cookie, backwards ? R_LAST : R_FIRST); #else (void) krb5_db2_db_unlock(context); return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ #endif } while (dbret == 0) { contdata.data = contents.data; contdata.length = contents.size; retval = krb5_decode_princ_contents(context, &contdata, &entries); if (retval) break; retval = (*func) (func_arg, &entries); krb5_dbe_free_contents(context, &entries); if (retval) break; if (!recursive) { dbret = (*db->seq) (db, &key, &contents, backwards ? R_PREV : R_NEXT); } else { #ifdef HAVE_BT_RSEQ dbret = bt_rseq(db, &key, &contents, &cookie, backwards ? R_PREV : R_NEXT); #else (void) krb5_db2_db_unlock(context); return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ #endif } } switch (dbret) { case 1: case 0: break; case -1: default: retval = errno; } (void) krb5_db2_db_unlock(context); return retval; } krb5_error_code krb5_db2_db_iterate(krb5_context context, char *match_expr, krb5_error_code(*func) (krb5_pointer, krb5_db_entry *), krb5_pointer func_arg, char **db_args) { char **t_ptr = db_args; int backwards = 0, recursive = 0; while (t_ptr && *t_ptr) { char *opt = NULL, *val = NULL; krb5_db2_get_db_opt(*t_ptr, &opt, &val); /* Solaris Kerberos: adding support for -rev/recurse flags */ if (val && !strcmp(val, "rev")) backwards = 1; else if (val && !strcmp(val, "recurse")) recursive = 1; else { krb5_set_error_message(context, EINVAL, gettext("Unsupported argument \"%s\" for db2"), val); free(opt); free(val); return EINVAL; } free(opt); free(val); t_ptr++; } /* Solaris Kerberos: adding support for -rev/recurse flags */ return krb5_db2_db_iterate_ext(context, func, func_arg, backwards, recursive); } krb5_boolean krb5_db2_db_set_lockmode(krb5_context context, krb5_boolean mode) { krb5_boolean old; krb5_db2_context *db_ctx; kdb5_dal_handle *dal_handle; dal_handle = (kdb5_dal_handle *) context->db_context; old = mode; if (dal_handle && (db_ctx = (krb5_db2_context *) dal_handle->db_context)) { old = db_ctx->db_nb_locks; db_ctx->db_nb_locks = mode; } return old; } /* * DAL API functions */ krb5_error_code krb5_db2_lib_init() { return 0; } krb5_error_code krb5_db2_lib_cleanup() { /* right now, no cleanup required */ return 0; } krb5_error_code krb5_db2_open(krb5_context kcontext, char *conf_section, char **db_args, int mode) { krb5_error_code status = 0; char **t_ptr = db_args; int db_name_set = 0, tempdb=0; char *dbname = NULL; krb5_clear_error_message (kcontext); if (k5db2_inited(kcontext)) return 0; while (t_ptr && *t_ptr) { char *opt = NULL, *val = NULL; krb5_db2_get_db_opt(*t_ptr, &opt, &val); if (opt && !strcmp(opt, "dbname")) { if (dbname) free(dbname); dbname = strdup(val); } else if (!opt && !strcmp(val, "temporary") ) { tempdb = 1; } /* ignore hash argument. Might have been passed from create */ else if (!opt || strcmp(opt, "hash")) { krb5_set_error_message(kcontext, EINVAL, gettext("Unsupported argument \"%s\" for db2"), opt ? opt : val); free(opt); free(val); return EINVAL; } free(opt); free(val); t_ptr++; } if(dbname) { status = krb5_db2_db_set_name(kcontext, dbname, tempdb); free(dbname); if (status) { /* Solaris Kerberos: Better error logging */ snprintf(errbuf, sizeof(errbuf), gettext("Failed to set db2 name to \"%s\": "), dbname); krb5_db2_prepend_err_str(kcontext, errbuf, status, status); goto clean_n_exit; } db_name_set = 1; } if (!db_name_set) { char *value = NULL; status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */ NULL, &value); if (value == NULL) { /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */ status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */ default_db_name, &value); if (status) { /* Solaris Kerberos: Better error logging */ snprintf(errbuf, sizeof(errbuf), gettext("Failed when searching for " "\"%s\", \"%s\", \"%s\" in profile: "), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME); krb5_db2_prepend_err_str(kcontext, errbuf, status, status); goto clean_n_exit; } } status = krb5_db2_db_set_name(kcontext, value, tempdb); if (status) { /* Solaris Kerberos: Better error logging */ snprintf(errbuf, sizeof(errbuf), gettext("Failed to set db2 name to \"%s\": "), value); krb5_db2_prepend_err_str(kcontext, errbuf, status, status); profile_release_string(value); goto clean_n_exit; } profile_release_string(value); } status = krb5_db2_db_init(kcontext); if (status) { /* Solaris Kerberos: Better error logging */ snprintf(errbuf, sizeof(errbuf), gettext("Failed to initialize db2 db: ")); krb5_db2_prepend_err_str(kcontext, errbuf, status, status); } clean_n_exit: return status; } krb5_error_code krb5_db2_create(krb5_context kcontext, char *conf_section, char **db_args) { krb5_error_code status = 0; char **t_ptr = db_args; int db_name_set = 0, tempdb=0; krb5_int32 flags = KRB5_KDB_CREATE_BTREE; char *db_name = NULL; krb5_clear_error_message (kcontext); if (k5db2_inited(kcontext)) return 0; while (t_ptr && *t_ptr) { char *opt = NULL, *val = NULL; krb5_db2_get_db_opt(*t_ptr, &opt, &val); if (opt && !strcmp(opt, "dbname")) { db_name = strdup(val); if (db_name == NULL) return ENOMEM; } else if (!opt && !strcmp(val, "temporary")) { tempdb = 1; } else if (opt && !strcmp(opt, "hash")) { flags = KRB5_KDB_CREATE_HASH; } else { krb5_set_error_message(kcontext, EINVAL, gettext("Unsupported argument \"%s\" for db2"), opt ? opt : val); free(opt); free(val); return EINVAL; } free(opt); free(val); t_ptr++; } if (db_name) { status = krb5_db2_db_set_name(kcontext, db_name, tempdb); if (!status) { status = EEXIST; goto clean_n_exit; } db_name_set = 1; } if (!db_name_set) { char *value = NULL; status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, /* under given conf section */ KDB_DB2_DATABASE_NAME, NULL, &value); if (value == NULL) { /* Special case for db2. We might actually be looking at * old type config file where database is specified as * part of realm. */ status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), /* under given realm */ KDB_DB2_DATABASE_NAME, default_db_name, &value); if (status) { goto clean_n_exit; } } db_name = strdup(value); /* Solaris Kerberos: for safety */ if (db_name == NULL) { status = ENOMEM; goto clean_n_exit; } status = krb5_db2_db_set_name(kcontext, value, tempdb); profile_release_string(value); if (!status) { status = EEXIST; goto clean_n_exit; } } status = krb5_db2_db_create(kcontext, db_name, flags); if (status) goto clean_n_exit; /* db2 has a problem of needing to close and open the database again. This removes that need */ status = krb5_db2_db_fini(kcontext); if (status) goto clean_n_exit; status = krb5_db2_open(kcontext, conf_section, db_args, KRB5_KDB_OPEN_RW); clean_n_exit: if (db_name) free(db_name); return status; } krb5_error_code krb5_db2_destroy(krb5_context kcontext, char *conf_section, char **db_args) { krb5_error_code status = 0; char **t_ptr = db_args; int db_name_set = 0, tempdb=0; char *db_name = NULL; while (t_ptr && *t_ptr) { char *opt = NULL, *val = NULL; krb5_db2_get_db_opt(*t_ptr, &opt, &val); if (opt && !strcmp(opt, "dbname")) { db_name = strdup(val); if (db_name == NULL) return ENOMEM; } else if (!opt && !strcmp(val, "temporary")) { tempdb = 1; } /* ignore hash argument. Might have been passed from create */ else if (!opt || strcmp(opt, "hash")) { free(opt); free(val); return EINVAL; } free(opt); free(val); t_ptr++; } if (db_name) { status = krb5_db2_db_set_name(kcontext, db_name, tempdb); if (status) { goto clean_n_exit; } db_name_set = 1; } if (!db_name_set) { char *value = NULL; status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */ NULL, &value); if (value == NULL) { /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */ status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */ default_db_name, &value); if (status) { goto clean_n_exit; } } db_name = strdup(value); if (db_name == NULL) { status = ENOMEM; goto clean_n_exit; } status = krb5_db2_db_set_name(kcontext, value, tempdb); profile_release_string(value); if (status) { goto clean_n_exit; } } status = krb5_db2_db_destroy(kcontext, db_name); clean_n_exit: if (db_name) free(db_name); return status; } krb5_error_code krb5_db2_set_master_key_ext(krb5_context kcontext, char *pwd, krb5_keyblock * key) { return krb5_db2_db_set_mkey(kcontext, key); } krb5_error_code krb5_db2_db_set_option(krb5_context kcontext, int option, void *value) { krb5_error_code status = 0; krb5_boolean oldval; krb5_db2_context *db_ctx; kdb5_dal_handle *dal_handle; if (!k5db2_inited(kcontext)) return KRB5_KDB_DBNOTINITED; dal_handle = (kdb5_dal_handle *) kcontext->db_context; db_ctx = (krb5_db2_context *) dal_handle->db_context; switch (option) { case KRB5_KDB_OPT_SET_DB_NAME: status = krb5_db2_db_set_name(kcontext, (char *) value, db_ctx->tempdb); break; case KRB5_KDB_OPT_SET_LOCK_MODE: oldval = krb5_db2_db_set_lockmode(kcontext, *((krb5_boolean *) value)); *((krb5_boolean *) value) = oldval; break; default: status = -1; /* TBD */ break; } return status; } void * krb5_db2_alloc(krb5_context kcontext, void *ptr, size_t size) { return realloc(ptr, size); } void krb5_db2_free(krb5_context kcontext, void *ptr) { free(ptr); } /* policy functions */ krb5_error_code krb5_db2_create_policy(krb5_context kcontext, osa_policy_ent_t policy) { kdb5_dal_handle *dal_handle; krb5_db2_context *dbc; dal_handle = (kdb5_dal_handle *) kcontext->db_context; dbc = (krb5_db2_context *) dal_handle->db_context; return osa_adb_create_policy(dbc->policy_db, policy); } krb5_error_code krb5_db2_get_policy(krb5_context kcontext, char *name, osa_policy_ent_t * policy, int *cnt) { kdb5_dal_handle *dal_handle; krb5_db2_context *dbc; dal_handle = (kdb5_dal_handle *) kcontext->db_context; dbc = (krb5_db2_context *) dal_handle->db_context; return osa_adb_get_policy(dbc->policy_db, name, policy, cnt); } krb5_error_code krb5_db2_put_policy(krb5_context kcontext, osa_policy_ent_t policy) { kdb5_dal_handle *dal_handle; krb5_db2_context *dbc; dal_handle = (kdb5_dal_handle *) kcontext->db_context; dbc = (krb5_db2_context *) dal_handle->db_context; return osa_adb_put_policy(dbc->policy_db, policy); } krb5_error_code krb5_db2_iter_policy(krb5_context kcontext, char *match_entry, osa_adb_iter_policy_func func, void *data) { kdb5_dal_handle *dal_handle; krb5_db2_context *dbc; dal_handle = (kdb5_dal_handle *) kcontext->db_context; dbc = (krb5_db2_context *) dal_handle->db_context; return osa_adb_iter_policy(dbc->policy_db, func, data); } krb5_error_code krb5_db2_delete_policy(krb5_context kcontext, char *policy) { kdb5_dal_handle *dal_handle; krb5_db2_context *dbc; dal_handle = (kdb5_dal_handle *) kcontext->db_context; dbc = (krb5_db2_context *) dal_handle->db_context; return osa_adb_destroy_policy(dbc->policy_db, policy); } void krb5_db2_free_policy(krb5_context kcontext, osa_policy_ent_t entry) { osa_free_policy_ent(entry); } /* */ krb5_error_code krb5_db2_promote_db(krb5_context kcontext, char *conf_section, char **db_args) { krb5_error_code status = 0; char *db_name = NULL; char *temp_db_name = NULL; krb5_clear_error_message (kcontext); { kdb5_dal_handle *dal_handle = kcontext->db_context; krb5_db2_context *db_ctx = dal_handle->db_context; db_name = strdup(db_ctx->db_name); if (db_name == NULL) { status = ENOMEM; goto clean_n_exit; } } assert(kcontext->db_context != NULL); temp_db_name = gen_dbsuffix(db_name, "~"); if (temp_db_name == NULL) { status = ENOMEM; goto clean_n_exit; } status = krb5_db2_db_rename (kcontext, temp_db_name, db_name); clean_n_exit: if (db_name) free(db_name); if (temp_db_name) free(temp_db_name); return status; } /* Retrieved from pre-DAL code base. */ /* * "Atomically" rename the database in a way that locks out read * access in the middle of the rename. * * Not perfect; if we crash in the middle of an update, we don't * necessarily know to complete the transaction the rename, but... * * Since the rename operation happens outside the init/fini bracket, we * have to go through the same stuff that we went through up in db_destroy. */ krb5_error_code krb5_db2_db_rename(context, from, to) krb5_context context; char *from; char *to; { char *fromok; krb5_error_code retval; krb5_db2_context *s_context, *db_ctx; kdb5_dal_handle *dal_handle = context->db_context; s_context = dal_handle->db_context; dal_handle->db_context = NULL; if ((retval = k5db2_init_context(context))) return retval; db_ctx = (krb5_db2_context *) dal_handle->db_context; /* * Create the database if it does not already exist; the * files must exist because krb5_db2_db_lock, called below, * will fail otherwise. */ { struct stat statbuf; if (stat(to, &statbuf) == -1) { if (errno == ENOENT) { retval = krb5_db2_db_create(context, to, KRB5_KDB_CREATE_BTREE); if (retval) goto errout; } else { /* * XXX assuming we should bail if there is some other stat error */ retval = errno; goto errout; } } } /* * Set the database to the target, so that other processes sharing * the target will stop their activity, and notice the new database. */ retval = krb5_db2_db_set_name(context, to, 0); if (retval) goto errout; retval = krb5_db2_db_init(context); if (retval) goto errout; /* XXX WAF this needs to be redone (not lock safe)!!! */ { /* Ugly brute force hack. Should be going through nice friendly helper routines for this, but it's a mess of jumbled so-called interfaces right now. */ char policy[2048], new_policy[2048]; assert (strlen(db_ctx->db_name) < 2000); /*LINTED*/ sprintf(policy, "%s.kadm5", db_ctx->db_name); /*LINTED*/ sprintf(new_policy, "%s~.kadm5", db_ctx->db_name); if (0 != rename(new_policy, policy)) { retval = errno; goto errout; } strcat(new_policy, ".lock"); (void) unlink(new_policy); } retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time); if (retval) goto errout; fromok = gen_dbsuffix(from, KDB2_LOCK_EXT); if (fromok == NULL) { retval = ENOMEM; goto errout; } if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) goto errfromok; if ((retval = krb5_db2_db_start_update(context))) goto errfromok; if (rename(from, to)) { retval = errno; goto errfromok; } if (unlink(fromok)) { retval = errno; goto errfromok; } retval = krb5_db2_db_end_update(context); errfromok: free_dbsuffix(fromok); errout: if (dal_handle->db_context) { if (db_ctx->db_lf_file >= 0) { krb5_db2_db_unlock(context); close(db_ctx->db_lf_file); } k5db2_clear_context((krb5_db2_context *) dal_handle->db_context); free(dal_handle->db_context); } dal_handle->db_context = s_context; (void) krb5_db2_db_unlock(context); /* unlock saved context db */ return retval; } const char * krb5_db2_errcode_2_string(krb5_context kcontext, long err_code) { return krb5_get_error_message(kcontext, err_code); } void krb5_db2_release_errcode_string(krb5_context kcontext, const char *msg) { krb5_free_error_message(kcontext, msg); } /* * Solaris Kerberos: * Similar to the ldap plugin. */ static void krb5_db2_prepend_err_str(krb5_context ctx, const char *str, krb5_error_code err, krb5_error_code oerr) { const char *omsg; if (oerr == 0) oerr = err; omsg = krb5_get_error_message (ctx, err); krb5_set_error_message (ctx, err, "%s %s", str, omsg); krb5_free_error_message(ctx, omsg); } /* * lib/kdb/kdb_db2.h * * Copyright 1997 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * * KDC Database backend definitions for Berkely DB. */ #ifndef KRB5_KDB_DB2_H #define KRB5_KDB_DB2_H #include "policy_db.h" typedef struct _krb5_db2_context { krb5_boolean db_inited; /* Context initialized */ char * db_name; /* Name of database */ DB * db; /* DB handle */ krb5_boolean hashfirst; /* Try hash database type first */ char * db_lf_name; /* Name of lock file */ int db_lf_file; /* File descriptor of lock file */ time_t db_lf_time; /* Time last updated */ int db_locks_held; /* Number of times locked */ int db_lock_mode; /* Last lock mode, e.g. greatest*/ krb5_boolean db_nb_locks; /* [Non]Blocking lock modes */ krb5_keyblock *db_master_key; /* Master key of database */ osa_adb_policy_t policy_db; krb5_boolean tempdb; } krb5_db2_context; #define KRB5_DB2_MAX_RETRY 5 #define KDB2_LOCK_EXT ".ok" #define KDB2_TEMP_LOCK_EXT "~.ok" krb5_error_code krb5_db2_db_init (krb5_context); krb5_error_code krb5_db2_db_fini (krb5_context); krb5_error_code krb5_db2_db_get_age (krb5_context, char *, time_t * ); krb5_error_code krb5_db2_db_create (krb5_context, char *, krb5_int32); krb5_error_code krb5_db2_db_destroy (krb5_context, char * ); krb5_error_code krb5_db2_db_rename (krb5_context, char *, char * ); krb5_error_code krb5_db2_db_get_principal (krb5_context, krb5_const_principal, krb5_db_entry *, int *, krb5_boolean * ); krb5_error_code krb5_db2_db_free_principal (krb5_context, krb5_db_entry *, int ); krb5_error_code krb5_db2_db_put_principal (krb5_context, krb5_db_entry *, int *, char **db_args ); krb5_error_code krb5_db2_db_iterate_ext (krb5_context, krb5_error_code (*) (krb5_pointer, krb5_db_entry *), krb5_pointer, int, int ); /* Solaris Kerberos: adding support for db_args */ krb5_error_code krb5_db2_db_iterate (krb5_context,char *, krb5_error_code (*) (krb5_pointer, krb5_db_entry *), krb5_pointer, char **db_args ); krb5_error_code krb5_db2_db_set_nonblocking (krb5_context, krb5_boolean, krb5_boolean * ); krb5_boolean krb5_db2_db_set_lockmode (krb5_context, krb5_boolean ); krb5_error_code krb5_db2_db_open_database (krb5_context); krb5_error_code krb5_db2_db_close_database (krb5_context); krb5_error_code krb5_db2_set_master_key_ext ( krb5_context kcontext, char *pwd, krb5_keyblock *key); krb5_error_code krb5_db2_db_set_mkey( krb5_context context, krb5_keyblock *key); krb5_error_code krb5_db2_db_get_mkey( krb5_context context, krb5_keyblock **key); krb5_error_code krb5_db2_db_put_principal( krb5_context context, krb5_db_entry *entries, register int *nentries, char **db_args); krb5_error_code krb5_db2_db_delete_principal(krb5_context context, krb5_const_principal searchfor, int *nentries); krb5_error_code krb5_db2_lib_init(void); krb5_error_code krb5_db2_lib_cleanup(void); krb5_error_code krb5_db2_db_unlock(krb5_context); krb5_error_code krb5_db2_promote_db(krb5_context kcontext, char *conf_section, char **db_args); krb5_error_code krb5_db2_db_set_option ( krb5_context kcontext, int option, void *value ); krb5_error_code krb5_db2_db_lock( krb5_context context, int in_mode); krb5_error_code krb5_db2_open( krb5_context kcontext, char *conf_section, char **db_args, int mode ); krb5_error_code krb5_db2_create( krb5_context kcontext, char *conf_section, char **db_args ); krb5_error_code krb5_db2_destroy( krb5_context kcontext, char *conf_section, char **db_args ); const char * krb5_db2_err2str( krb5_context kcontext, long err_code ); void * krb5_db2_alloc( krb5_context kcontext, void *ptr, size_t size ); void krb5_db2_free( krb5_context kcontext, void *ptr ); const char * krb5_db2_errcode_2_string(krb5_context kcontext, long err_code); void krb5_db2_release_errcode_string(krb5_context kcontext, const char *msg); /* policy management functions */ krb5_error_code krb5_db2_create_policy(krb5_context context, osa_policy_ent_t entry); krb5_error_code krb5_db2_get_policy ( krb5_context kcontext, char *name, osa_policy_ent_t *policy, int *cnt); krb5_error_code krb5_db2_put_policy ( krb5_context kcontext, osa_policy_ent_t policy ); krb5_error_code krb5_db2_iter_policy ( krb5_context kcontext, char *match_entry, osa_adb_iter_policy_func func, void *data ); krb5_error_code krb5_db2_delete_policy ( krb5_context kcontext, char *policy ); void krb5_db2_free_policy( krb5_context kcontext, osa_policy_ent_t entry ); #endif /* KRB5_KDB_DB2_H */ /* * lib/kdb/kdb_xdr.c * * Copyright 1995 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ #include "k5-int.h" #include #include #include #include "kdb_xdr.h" krb5_error_code krb5_encode_princ_dbkey(context, key, principal) krb5_context context; krb5_data *key; krb5_const_principal principal; { char *princ_name; krb5_error_code retval; if (!(retval = krb5_unparse_name(context, principal, &princ_name))) { /* need to store the NULL for decoding */ key->length = strlen(princ_name)+1; key->data = princ_name; } return(retval); } void krb5_free_princ_dbkey(context, key) krb5_context context; krb5_data *key; { (void) krb5_free_data_contents(context, key); } krb5_error_code krb5_encode_princ_contents(context, content, entry) krb5_context context; krb5_data * content; krb5_db_entry * entry; { int i, j; unsigned int unparse_princ_size; char * unparse_princ; char * nextloc; krb5_tl_data * tl_data; krb5_error_code retval; krb5_int16 psize16; /* * Generate one lump of data from the krb5_db_entry. * This data must be independent of byte order of the machine, * compact and extensible. */ /* * First allocate enough space for all the data. * Need 2 bytes for the length of the base structure * then 36 [ 8 * 4 + 2 * 2] bytes for the base information * [ attributes, max_life, max_renewable_life, expiration, * pw_expiration, last_success, last_failed, fail_auth_count ] * [ n_key_data, n_tl_data ] * then XX bytes [ e_length ] for the extra data [ e_data ] * then XX bytes [ 2 for length + length for string ] for the principal, * then (4 [type + length] + tl_data_length) bytes per tl_data * then (4 + (4 + key_data_length) per key_data_contents) bytes per key_data */ content->length = entry->len + entry->e_length; if ((retval = krb5_unparse_name(context, entry->princ, &unparse_princ))) return(retval); unparse_princ_size = strlen(unparse_princ) + 1; content->length += unparse_princ_size; content->length += 2; i = 0; /* tl_data is a linked list */ for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) { content->length += tl_data->tl_data_length; content->length += 4; /* type, length */ i++; } if (i != entry->n_tl_data) { retval = KRB5_KDB_TRUNCATED_RECORD; goto epc_error; } /* key_data is an array */ for (i = 0; i < entry->n_key_data; i++) { content->length += 4; /* Version, KVNO */ for (j = 0; j < entry->key_data[i].key_data_ver; j++) { content->length += entry->key_data[i].key_data_length[j]; content->length += 4; /* type + length */ } } if ((content->data = malloc(content->length)) == NULL) { retval = ENOMEM; goto epc_error; } /* * Now we go through entry again, this time copying data * These first entries are always saved regardless of version */ nextloc = content->data; /* Base Length */ krb5_kdb_encode_int16(entry->len, nextloc); nextloc += 2; /* Attributes */ krb5_kdb_encode_int32(entry->attributes, nextloc); nextloc += 4; /* Max Life */ krb5_kdb_encode_int32(entry->max_life, nextloc); nextloc += 4; /* Max Renewable Life */ krb5_kdb_encode_int32(entry->max_renewable_life, nextloc); nextloc += 4; /* When the client expires */ krb5_kdb_encode_int32(entry->expiration, nextloc); nextloc += 4; /* When its passwd expires */ krb5_kdb_encode_int32(entry->pw_expiration, nextloc); nextloc += 4; /* Last successful passwd */ krb5_kdb_encode_int32(entry->last_success, nextloc); nextloc += 4; /* Last failed passwd attempt */ krb5_kdb_encode_int32(entry->last_failed, nextloc); nextloc += 4; /* # of failed passwd attempt */ krb5_kdb_encode_int32(entry->fail_auth_count, nextloc); nextloc += 4; /* # tl_data strutures */ krb5_kdb_encode_int16(entry->n_tl_data, nextloc); nextloc += 2; /* # key_data strutures */ krb5_kdb_encode_int16(entry->n_key_data, nextloc); nextloc += 2; /* Put extended fields here */ if (entry->len != KRB5_KDB_V1_BASE_LENGTH) abort(); /* Any extra data that this version doesn't understand. */ if (entry->e_length) { memcpy(nextloc, entry->e_data, entry->e_length); nextloc += entry->e_length; } /* * Now we get to the principal. * To squeze a few extra bytes out it is always assumed to come * after the base type. */ psize16 = (krb5_int16) unparse_princ_size; krb5_kdb_encode_int16(psize16, nextloc); nextloc += 2; (void) memcpy(nextloc, unparse_princ, unparse_princ_size); nextloc += unparse_princ_size; /* tl_data is a linked list, of type, legth, contents */ for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) { krb5_kdb_encode_int16(tl_data->tl_data_type, nextloc); nextloc += 2; krb5_kdb_encode_int16(tl_data->tl_data_length, nextloc); nextloc += 2; memcpy(nextloc, tl_data->tl_data_contents, tl_data->tl_data_length); nextloc += tl_data->tl_data_length; } /* key_data is an array */ for (i = 0; i < entry->n_key_data; i++) { krb5_kdb_encode_int16(entry->key_data[i].key_data_ver, nextloc); nextloc += 2; krb5_kdb_encode_int16(entry->key_data[i].key_data_kvno, nextloc); nextloc += 2; for (j = 0; j < entry->key_data[i].key_data_ver; j++) { krb5_int16 type = entry->key_data[i].key_data_type[j]; krb5_ui_2 length = entry->key_data[i].key_data_length[j]; krb5_kdb_encode_int16(type, nextloc); nextloc += 2; krb5_kdb_encode_int16(length, nextloc); nextloc += 2; if (length) { memcpy(nextloc, entry->key_data[i].key_data_contents[j],length); nextloc += length; } } } epc_error:; free(unparse_princ); return retval; } void krb5_free_princ_contents(context, contents) krb5_context context; krb5_data *contents; { krb5_free_data_contents(context, contents); return; } krb5_error_code krb5_decode_princ_contents(context, content, entry) krb5_context context; krb5_data * content; krb5_db_entry * entry; { int sizeleft, i; char * nextloc; krb5_tl_data ** tl_data; krb5_int16 i16; krb5_error_code retval; /* Zero out entry and NULL pointers */ memset(entry, 0, sizeof(krb5_db_entry)); /* * undo the effects of encode_princ_contents. * * The first part is decoding the base type. If the base type is * bigger than the original base type then the additional fields * need to be filled in. If the base type is larger than any * known base type the additional data goes in e_data. */ /* First do the easy stuff */ nextloc = content->data; sizeleft = content->length; if ((sizeleft -= KRB5_KDB_V1_BASE_LENGTH) < 0) return KRB5_KDB_TRUNCATED_RECORD; /* Base Length */ krb5_kdb_decode_int16(nextloc, entry->len); nextloc += 2; /* Attributes */ krb5_kdb_decode_int32(nextloc, entry->attributes); nextloc += 4; /* Max Life */ krb5_kdb_decode_int32(nextloc, entry->max_life); nextloc += 4; /* Max Renewable Life */ krb5_kdb_decode_int32(nextloc, entry->max_renewable_life); nextloc += 4; /* When the client expires */ krb5_kdb_decode_int32(nextloc, entry->expiration); nextloc += 4; /* When its passwd expires */ krb5_kdb_decode_int32(nextloc, entry->pw_expiration); nextloc += 4; /* Last successful passwd */ krb5_kdb_decode_int32(nextloc, entry->last_success); nextloc += 4; /* Last failed passwd attempt */ krb5_kdb_decode_int32(nextloc, entry->last_failed); nextloc += 4; /* # of failed passwd attempt */ krb5_kdb_decode_int32(nextloc, entry->fail_auth_count); nextloc += 4; /* # tl_data strutures */ krb5_kdb_decode_int16(nextloc, entry->n_tl_data); nextloc += 2; if (entry->n_tl_data < 0) return KRB5_KDB_TRUNCATED_RECORD; /* # key_data strutures */ krb5_kdb_decode_int16(nextloc, entry->n_key_data); nextloc += 2; if (entry->n_key_data < 0) return KRB5_KDB_TRUNCATED_RECORD; /* Check for extra data */ if (entry->len > KRB5_KDB_V1_BASE_LENGTH) { entry->e_length = entry->len - KRB5_KDB_V1_BASE_LENGTH; if ((entry->e_data = (krb5_octet *)malloc(entry->e_length))) { memcpy(entry->e_data, nextloc, entry->e_length); nextloc += entry->e_length; } else { return ENOMEM; } } /* * Get the principal name for the entry * (stored as a string which gets unparsed.) */ if ((sizeleft -= 2) < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } i = 0; krb5_kdb_decode_int16(nextloc, i16); i = (int) i16; nextloc += 2; if ((retval = krb5_parse_name(context, nextloc, &(entry->princ)))) goto error_out; if (((size_t) i != (strlen(nextloc) + 1)) || (sizeleft < i)) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } sizeleft -= i; nextloc += i; /* tl_data is a linked list */ tl_data = &entry->tl_data; for (i = 0; i < entry->n_tl_data; i++) { if ((sizeleft -= 4) < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } if ((*tl_data = (krb5_tl_data *) malloc(sizeof(krb5_tl_data))) == NULL) { retval = ENOMEM; goto error_out; } (*tl_data)->tl_data_next = NULL; (*tl_data)->tl_data_contents = NULL; krb5_kdb_decode_int16(nextloc, (*tl_data)->tl_data_type); nextloc += 2; krb5_kdb_decode_int16(nextloc, (*tl_data)->tl_data_length); nextloc += 2; if ((sizeleft -= (*tl_data)->tl_data_length) < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } if (((*tl_data)->tl_data_contents = (krb5_octet *) malloc((*tl_data)->tl_data_length)) == NULL) { retval = ENOMEM; goto error_out; } memcpy((*tl_data)->tl_data_contents,nextloc,(*tl_data)->tl_data_length); nextloc += (*tl_data)->tl_data_length; tl_data = &((*tl_data)->tl_data_next); } /* key_data is an array */ if (entry->n_key_data && ((entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * entry->n_key_data)) == NULL)) { retval = ENOMEM; goto error_out; } for (i = 0; i < entry->n_key_data; i++) { krb5_key_data * key_data; int j; if ((sizeleft -= 4) < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } key_data = entry->key_data + i; memset(key_data, 0, sizeof(krb5_key_data)); krb5_kdb_decode_int16(nextloc, key_data->key_data_ver); nextloc += 2; krb5_kdb_decode_int16(nextloc, key_data->key_data_kvno); nextloc += 2; /* key_data_ver determins number of elements and how to unparse them. */ if (key_data->key_data_ver <= KRB5_KDB_V1_KEY_DATA_ARRAY) { for (j = 0; j < key_data->key_data_ver; j++) { if ((sizeleft -= 4) < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } krb5_kdb_decode_int16(nextloc, key_data->key_data_type[j]); nextloc += 2; krb5_kdb_decode_int16(nextloc, key_data->key_data_length[j]); nextloc += 2; if ((sizeleft -= key_data->key_data_length[j]) < 0) { retval = KRB5_KDB_TRUNCATED_RECORD; goto error_out; } if (key_data->key_data_length[j]) { if ((key_data->key_data_contents[j] = (krb5_octet *) malloc(key_data->key_data_length[j])) == NULL) { retval = ENOMEM; goto error_out; } memcpy(key_data->key_data_contents[j], nextloc, key_data->key_data_length[j]); nextloc += key_data->key_data_length[j]; } } } else { /* This isn't right. I'll fix it later */ abort(); } } return 0; error_out:; krb5_dbe_free_contents(context, entry); return retval; } void krb5_dbe_free_contents(context, entry) krb5_context context; krb5_db_entry * entry; { krb5_tl_data * tl_data_next; krb5_tl_data * tl_data; int i, j; if (entry->e_data) free(entry->e_data); if (entry->princ) krb5_free_principal(context, entry->princ); for (tl_data = entry->tl_data; tl_data; tl_data = tl_data_next) { tl_data_next = tl_data->tl_data_next; if (tl_data->tl_data_contents) free(tl_data->tl_data_contents); free(tl_data); } if (entry->key_data) { for (i = 0; i < entry->n_key_data; i++) { for (j = 0; j < entry->key_data[i].key_data_ver; j++) { if (entry->key_data[i].key_data_length[j]) { if (entry->key_data[i].key_data_contents[j]) { memset(entry->key_data[i].key_data_contents[j], 0, (unsigned) entry->key_data[i].key_data_length[j]); free (entry->key_data[i].key_data_contents[j]); } } entry->key_data[i].key_data_contents[j] = NULL; entry->key_data[i].key_data_length[j] = 0; entry->key_data[i].key_data_type[j] = 0; } } free(entry->key_data); } memset(entry, 0, sizeof(*entry)); return; } #ifndef _KDB2_XDR_H #define _KDB2_XDR_H #include "kdb.h" krb5_error_code krb5_encode_princ_dbkey( krb5_context context, krb5_data *key, krb5_const_principal principal); krb5_error_code krb5_decode_princ_contents( krb5_context context, krb5_data * content, krb5_db_entry * entry); void krb5_dbe_free_contents( krb5_context context, krb5_db_entry * entry); krb5_error_code krb5_encode_princ_contents( krb5_context context, krb5_data * content, krb5_db_entry * entry); void krb5_free_princ_dbkey( krb5_context context, krb5_data *key); void krb5_free_princ_contents( krb5_context context, krb5_data *contents); #endif # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include $(SRC)/lib/krb5/Makefile.lib include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 # Hammerhead: amd64-only SUBDIRS= $(MACH64) all : TARGET= all clean : TARGET= clean clobber : TARGET= clobber install : TARGET= install _msg : TARGET= _msg README= README.db2 ROOTREADME= $(README:%=$(ROOTLIBDIR)/%) $(ROOTLIBDIR)/$(README) : FILEMODE= 444 .KEEP_STATE: all clean clobber _msg: $(SUBDIRS) $(ROOTLIBDIR): $(INS.dir) $(ROOTLIBDIR)/%: % $(INS.file) install: $(ROOTLIBDIR) $(ROOTREADME) $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. LIBRARY= libdb2.a VERS= .1 # btree BTREE_OBJS= \ bt_close.o \ bt_conv.o \ bt_debug.o \ bt_delete.o \ bt_get.o \ bt_open.o \ bt_overflow.o \ bt_page.o \ bt_put.o \ bt_search.o \ bt_seq.o \ bt_split.o \ bt_utils.o # db DB_OBJS= db.o # hash HASH_OBJS= \ hash.o \ hash_bigkey.o \ hash_func.o \ hash_log2.o \ hash_page.o \ hsearch.o \ dbm.o # mpool MPOOL_OBJS= mpool.o # recno RECNO_OBJS= \ rec_close.o \ rec_delete.o \ rec_get.o \ rec_open.o \ rec_put.o \ rec_search.o \ rec_seq.o \ rec_utils.o OBJECTS= \ $(BTREE_OBJS) $(DB_OBJS) $(HASH_OBJS) $(MPOOL_OBJS) $(RECNO_OBJS) # include library definitions include $(SRC)/lib/krb5/Makefile.lib SRCS= $(BTREE_OBJS:%.o=../btree/%.c) \ $(DB_OBJS:%.o=../db/%.c) \ $(HASH_OBJS:%.o=../hash/%.c) \ $(MPOOL_OBJS:%.o=../mpool/%.c) \ $(RECNO_OBJS:%.o=../recno/%.c) LIBS= $(DYNLIB) include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 POFILE = $(LIBRARY:%.a=%.po) POFILES = generic.po #override liblink INS.liblink= -$(RM) $@; $(SYMLINK) $(LIBLINKS)$(VERS) $@ CPPFLAGS += -DHAVE_CONFIG_H \ -I$(SRC)/lib/krb5/plugins/kdb/db2/libdb2/mpool \ -I$(SRC)/lib/krb5/plugins/kdb/db2/libdb2/db \ -I$(SRC)/lib/krb5/plugins/kdb/db2/libdb2/hash \ -I$(SRC)/lib/krb5/plugins/kdb/db2/libdb2/btree \ -I$(SRC)/lib/krb5/plugins/kdb/db2/libdb2/recno \ -I$(SRC)/lib/krb5/plugins/kdb/db2/libdb2/include \ -I$(SRC)/lib/gss_mechs/mech_krb5/include #for db-ndbm.h # Hammerhead: -I.. must be in CPPFLAGS (not CFLAGS) to survive Makefile.master.64 reset CFLAGS += $(CCVERBOSE) CPPFLAGS += -I.. CERRWARN += $(CNOWARN_UNINIT) # not linted SMATCH=off LDLIBS += -lc # Identify that this library is an interposer (on dbm_ routines from libc.so.1). # This identification insures runtime symbol lookup resolves to this library # (before libc.so.1) regardless of dependency link order. DYNFLAGS += $(ZINTERPOSE) .KEEP_STATE: all: $(LIBS) # include library targets include $(SRC)/lib/krb5/Makefile.targ pics/%.o: ../btree/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) pics/%.o: ../db/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) pics/%.o: ../hash/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) pics/%.o: ../mpool/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) pics/%.o: ../recno/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) FRC: generic.po: FRC $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext ../hash/*.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po # ident "%Z%%M% %I% %E% SMI" /usr/lib/krb5/libdb2.so.1 is the DB package that is shipped with MIT Kerberos 1.0. It is *not* Berkeley DB 1.x or Sleepycat DB 2.x and is incompatible with both. Its use is intended solely for the Solaris SEAM (Kerberos) components and it is not a public interface thus any other use is *unsupported* and it may be removed from Solaris at any time without warning. # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2025 Hammerhead Project # include ../Makefile.com include $(SRC)/lib/Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_close.c 8.7 (Berkeley) 8/17/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include #include "db-int.h" #include "btree.h" static int bt_meta __P((BTREE *)); /* * BT_CLOSE -- Close a btree. * * Parameters: * dbp: pointer to access method * * Returns: * RET_ERROR, RET_SUCCESS */ int __bt_close(dbp) DB *dbp; { BTREE *t; int fd; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } /* Sync the tree. */ if (__bt_sync(dbp, 0) == RET_ERROR) return (RET_ERROR); /* Close the memory pool. */ if (mpool_close(t->bt_mp) == RET_ERROR) return (RET_ERROR); /* Free random memory. */ if (t->bt_cursor.key.data != NULL) { free(t->bt_cursor.key.data); t->bt_cursor.key.size = 0; t->bt_cursor.key.data = NULL; } if (t->bt_rkey.data) { free(t->bt_rkey.data); t->bt_rkey.size = 0; t->bt_rkey.data = NULL; } if (t->bt_rdata.data) { free(t->bt_rdata.data); t->bt_rdata.size = 0; t->bt_rdata.data = NULL; } fd = t->bt_fd; free(t); free(dbp); return (close(fd) ? RET_ERROR : RET_SUCCESS); } /* * BT_SYNC -- sync the btree to disk. * * Parameters: * dbp: pointer to access method * * Returns: * RET_SUCCESS, RET_ERROR. */ int __bt_sync(dbp, flags) const DB *dbp; u_int flags; { BTREE *t; int status; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } /* Sync doesn't currently take any flags. */ if (flags != 0) { errno = EINVAL; return (RET_ERROR); } if (F_ISSET(t, B_INMEM | B_RDONLY) || !F_ISSET(t, B_MODIFIED | B_METADIRTY)) return (RET_SUCCESS); if (F_ISSET(t, B_METADIRTY) && bt_meta(t) == RET_ERROR) return (RET_ERROR); if ((status = mpool_sync(t->bt_mp)) == RET_SUCCESS) F_CLR(t, B_MODIFIED); return (status); } /* * BT_META -- write the tree meta data to disk. * * Parameters: * t: tree * * Returns: * RET_ERROR, RET_SUCCESS */ static int bt_meta(t) BTREE *t; { BTMETA m; void *p; if ((p = mpool_get(t->bt_mp, P_META, 0)) == NULL) return (RET_ERROR); /* Fill in metadata. */ m.magic = BTREEMAGIC; m.version = BTREEVERSION; m.psize = t->bt_psize; m.free = t->bt_free; m.nrecs = t->bt_nrecs; m.flags = F_ISSET(t, SAVEMETA); memmove(p, &m, sizeof(BTMETA)); mpool_put(t->bt_mp, p, MPOOL_DIRTY); return (RET_SUCCESS); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_conv.c 8.5 (Berkeley) 8/17/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include "db-int.h" #include "btree.h" static void mswap __P((PAGE *)); /* * __BT_BPGIN, __BT_BPGOUT -- * Convert host-specific number layout to/from the host-independent * format stored on disk. * * Parameters: * t: tree * pg: page number * h: page to convert */ void __bt_pgin(t, pg, pp) void *t; db_pgno_t pg; void *pp; { PAGE *h; indx_t i, top; u_char flags; char *p; if (!F_ISSET(((BTREE *)t), B_NEEDSWAP)) return; if (pg == P_META) { mswap(pp); return; } h = pp; M_32_SWAP(h->pgno); M_32_SWAP(h->prevpg); M_32_SWAP(h->nextpg); M_32_SWAP(h->flags); M_16_SWAP(h->lower); M_16_SWAP(h->upper); top = NEXTINDEX(h); if ((h->flags & P_TYPE) == P_BINTERNAL) for (i = 0; i < top; i++) { M_16_SWAP(h->linp[i]); p = (char *)GETBINTERNAL(h, i); P_32_SWAP(p); p += sizeof(u_int32_t); P_32_SWAP(p); p += sizeof(db_pgno_t); if (*(u_char *)p & P_BIGKEY) { p += sizeof(u_char); P_32_SWAP(p); p += sizeof(db_pgno_t); P_32_SWAP(p); } } else if ((h->flags & P_TYPE) == P_BLEAF) for (i = 0; i < top; i++) { M_16_SWAP(h->linp[i]); p = (char *)GETBLEAF(h, i); P_32_SWAP(p); p += sizeof(u_int32_t); P_32_SWAP(p); p += sizeof(u_int32_t); flags = *(u_char *)p; if (flags & (P_BIGKEY | P_BIGDATA)) { p += sizeof(u_char); if (flags & P_BIGKEY) { P_32_SWAP(p); p += sizeof(db_pgno_t); P_32_SWAP(p); } if (flags & P_BIGDATA) { p += sizeof(u_int32_t); P_32_SWAP(p); p += sizeof(db_pgno_t); P_32_SWAP(p); } } } } void __bt_pgout(t, pg, pp) void *t; db_pgno_t pg; void *pp; { PAGE *h; indx_t i, top; u_char flags; char *p; if (!F_ISSET(((BTREE *)t), B_NEEDSWAP)) return; if (pg == P_META) { mswap(pp); return; } h = pp; top = NEXTINDEX(h); if ((h->flags & P_TYPE) == P_BINTERNAL) for (i = 0; i < top; i++) { p = (char *)GETBINTERNAL(h, i); P_32_SWAP(p); p += sizeof(u_int32_t); P_32_SWAP(p); p += sizeof(db_pgno_t); if (*(u_char *)p & P_BIGKEY) { p += sizeof(u_char); P_32_SWAP(p); p += sizeof(db_pgno_t); P_32_SWAP(p); } M_16_SWAP(h->linp[i]); } else if ((h->flags & P_TYPE) == P_BLEAF) for (i = 0; i < top; i++) { p = (char *)GETBLEAF(h, i); P_32_SWAP(p); p += sizeof(u_int32_t); P_32_SWAP(p); p += sizeof(u_int32_t); flags = *(u_char *)p; if (flags & (P_BIGKEY | P_BIGDATA)) { p += sizeof(u_char); if (flags & P_BIGKEY) { P_32_SWAP(p); p += sizeof(db_pgno_t); P_32_SWAP(p); } if (flags & P_BIGDATA) { p += sizeof(u_int32_t); P_32_SWAP(p); p += sizeof(db_pgno_t); P_32_SWAP(p); } } M_16_SWAP(h->linp[i]); } M_32_SWAP(h->pgno); M_32_SWAP(h->prevpg); M_32_SWAP(h->nextpg); M_32_SWAP(h->flags); M_16_SWAP(h->lower); M_16_SWAP(h->upper); } /* * MSWAP -- Actually swap the bytes on the meta page. * * Parameters: * p: page to convert */ static void mswap(pg) PAGE *pg; { char *p; p = (char *)pg; P_32_SWAP(p); /* magic */ p += sizeof(u_int32_t); P_32_SWAP(p); /* version */ p += sizeof(u_int32_t); P_32_SWAP(p); /* psize */ p += sizeof(u_int32_t); P_32_SWAP(p); /* free */ p += sizeof(u_int32_t); P_32_SWAP(p); /* nrecs */ p += sizeof(u_int32_t); P_32_SWAP(p); /* flags */ p += sizeof(u_int32_t); } /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /*- * Copyright (c) 1990, 1993, 1994, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_debug.c 8.6 (Berkeley) 1/9/95"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include "db-int.h" #include "btree.h" #if defined(DEBUG) || defined(STATISTICS) static FILE *tracefp; /* * __bt_dinit -- * initialize debugging. */ static void __bt_dinit() { static int first = 1; char buf[1024]; if (!first) return; first = 0; #ifndef TRACE_TO_STDERR if ((tracefp = fopen("/tmp/__bt_debug", "wF")) != NULL) return; #endif tracefp = stderr; } #endif #ifdef DEBUG /* * __bt_dump -- * dump the tree * * Parameters: * dbp: pointer to the DB */ int __bt_dump(dbp) DB *dbp; { BTREE *t; PAGE *h; db_pgno_t i; char *sep; __bt_dinit(); t = dbp->internal; (void)fprintf(tracefp, "%s: pgsz %d", F_ISSET(t, B_INMEM) ? "memory" : "disk", t->bt_psize); if (F_ISSET(t, R_RECNO)) (void)fprintf(tracefp, " keys %lu", t->bt_nrecs); #undef X #define X(flag, name) \ if (F_ISSET(t, flag)) { \ (void)fprintf(tracefp, "%s%s", sep, name); \ sep = ", "; \ } if (t->flags != 0) { sep = " flags ("; X(R_FIXLEN, "FIXLEN"); X(B_INMEM, "INMEM"); X(B_NODUPS, "NODUPS"); X(B_RDONLY, "RDONLY"); X(R_RECNO, "RECNO"); X(B_METADIRTY,"METADIRTY"); (void)fprintf(tracefp, ")\n"); } #undef X for (i = P_ROOT; i < t->bt_mp->npages && (h = mpool_get(t->bt_mp, i, MPOOL_IGNOREPIN)) != NULL; ++i) __bt_dpage(dbp, h); (void)fflush(tracefp); return (0); } /* * BT_DMPAGE -- Dump the meta page * * Parameters: * h: pointer to the PAGE */ int __bt_dmpage(h) PAGE *h; { BTMETA *m; char *sep; __bt_dinit(); m = (BTMETA *)h; (void)fprintf(tracefp, "magic %lx\n", m->magic); (void)fprintf(tracefp, "version %lu\n", m->version); (void)fprintf(tracefp, "psize %lu\n", m->psize); (void)fprintf(tracefp, "free %lu\n", m->free); (void)fprintf(tracefp, "nrecs %lu\n", m->nrecs); (void)fprintf(tracefp, "flags %lu", m->flags); #undef X #define X(flag, name) \ if (m->flags & flag) { \ (void)fprintf(tracefp, "%s%s", sep, name); \ sep = ", "; \ } if (m->flags) { sep = " ("; X(B_NODUPS, "NODUPS"); X(R_RECNO, "RECNO"); (void)fprintf(tracefp, ")"); } (void)fprintf(tracefp, "\n"); (void)fflush(tracefp); return (0); } /* * BT_DNPAGE -- Dump the page * * Parameters: * n: page number to dump. */ int __bt_dnpage(dbp, pgno) DB *dbp; db_pgno_t pgno; { BTREE *t; PAGE *h; __bt_dinit(); t = dbp->internal; if ((h = mpool_get(t->bt_mp, pgno, MPOOL_IGNOREPIN)) != NULL) __bt_dpage(dbp, h); (void)fflush(tracefp); return (0); } /* * BT_DPAGE -- Dump the page * * Parameters: * h: pointer to the PAGE */ void __bt_dpage(dbp, h) DB *dbp; PAGE *h; { BINTERNAL *bi; BLEAF *bl; RINTERNAL *ri; RLEAF *rl; u_long pgsize; indx_t cur, top, lim; char *sep; __bt_dinit(); (void)fprintf(tracefp, " page %d: (", h->pgno); #undef X #define X(flag, name) \ if (h->flags & flag) { \ (void)fprintf(tracefp, "%s%s", sep, name); \ sep = ", "; \ } sep = ""; X(P_BINTERNAL, "BINTERNAL") /* types */ X(P_BLEAF, "BLEAF") X(P_RINTERNAL, "RINTERNAL") /* types */ X(P_RLEAF, "RLEAF") X(P_OVERFLOW, "OVERFLOW") X(P_PRESERVE, "PRESERVE"); (void)fprintf(tracefp, ")\n"); #undef X (void)fprintf(tracefp, "\tprev %2d next %2d", h->prevpg, h->nextpg); if (h->flags & P_OVERFLOW) return; pgsize = ((BTREE *)dbp->internal)->bt_mp->pagesize; lim = (pgsize - BTDATAOFF) / sizeof(indx_t); top = NEXTINDEX(h); lim = top > lim ? lim : top; (void)fprintf(tracefp, " lower %3d upper %3d nextind %d\n", h->lower, h->upper, top); for (cur = 0; cur < lim; cur++) { (void)fprintf(tracefp, "\t[%03d] %4d ", cur, h->linp[cur]); switch (h->flags & P_TYPE) { case P_BINTERNAL: bi = GETBINTERNAL(h, cur); (void)fprintf(tracefp, "size %03d pgno %03d", bi->ksize, bi->pgno); if (bi->flags & P_BIGKEY) (void)fprintf(tracefp, " (indirect)"); else if (bi->ksize) (void)fprintf(tracefp, " {%.*s}", (int)bi->ksize, bi->bytes); break; case P_RINTERNAL: ri = GETRINTERNAL(h, cur); (void)fprintf(tracefp, "entries %03d pgno %03d", ri->nrecs, ri->pgno); break; case P_BLEAF: bl = GETBLEAF(h, cur); if (bl->flags & P_BIGKEY) (void)fprintf(tracefp, "big key page %lu size %u/", *(db_pgno_t *)bl->bytes, *(u_int32_t *)(bl->bytes + sizeof(db_pgno_t))); else if (bl->ksize) (void)fprintf(tracefp, "%s/", bl->bytes); if (bl->flags & P_BIGDATA) (void)fprintf(tracefp, "big data page %lu size %u", *(db_pgno_t *)(bl->bytes + bl->ksize), *(u_int32_t *)(bl->bytes + bl->ksize + sizeof(db_pgno_t))); else if (bl->dsize) (void)fprintf(tracefp, "%.*s", (int)bl->dsize, bl->bytes + bl->ksize); break; case P_RLEAF: rl = GETRLEAF(h, cur); if (rl->flags & P_BIGDATA) (void)fprintf(tracefp, "big data page %lu size %u", *(db_pgno_t *)rl->bytes, *(u_int32_t *)(rl->bytes + sizeof(db_pgno_t))); else if (rl->dsize) (void)fprintf(tracefp, "%.*s", (int)rl->dsize, rl->bytes); break; } (void)fprintf(tracefp, "\n"); } (void)fflush(tracefp); return; } #endif #ifdef STATISTICS /* * bt_stat -- * Gather/print the tree statistics * * Parameters: * dbp: pointer to the DB */ int __bt_stat(dbp) DB *dbp; { extern u_long bt_cache_hit, bt_cache_miss, bt_pfxsaved, bt_rootsplit; extern u_long bt_sortsplit, bt_split; BTREE *t; PAGE *h; db_pgno_t i, pcont, pinternal, pleaf; u_long ifree, lfree, nkeys; int levels; __bt_dinit(); t = dbp->internal; pcont = pinternal = pleaf = 0; nkeys = ifree = lfree = 0; for (i = P_ROOT; i < t->bt_mp->npages && (h = mpool_get(t->bt_mp, i, MPOOL_IGNOREPIN)) != NULL; ++i) switch (h->flags & P_TYPE) { case P_BINTERNAL: case P_RINTERNAL: ++pinternal; ifree += h->upper - h->lower; break; case P_BLEAF: case P_RLEAF: ++pleaf; lfree += h->upper - h->lower; nkeys += NEXTINDEX(h); break; case P_OVERFLOW: ++pcont; break; } /* Count the levels of the tree. */ for (i = P_ROOT, levels = 0 ;; ++levels) { h = mpool_get(t->bt_mp, i, MPOOL_IGNOREPIN); if (h->flags & (P_BLEAF|P_RLEAF)) { if (levels == 0) levels = 1; break; } i = F_ISSET(t, R_RECNO) ? GETRINTERNAL(h, 0)->pgno : GETBINTERNAL(h, 0)->pgno; } (void)fprintf(tracefp, "%d level%s with %ld keys", levels, levels == 1 ? "" : "s", nkeys); if (F_ISSET(t, R_RECNO)) (void)fprintf(tracefp, " (%ld header count)", t->bt_nrecs); (void)fprintf(tracefp, "\n%lu pages (leaf %ld, internal %ld, overflow %ld)\n", pinternal + pleaf + pcont, pleaf, pinternal, pcont); (void)fprintf(tracefp, "%ld cache hits, %ld cache misses\n", bt_cache_hit, bt_cache_miss); (void)fprintf(tracefp, "%ld splits (%ld root splits, %ld sort splits)\n", bt_split, bt_rootsplit, bt_sortsplit); pleaf *= t->bt_psize - BTDATAOFF; if (pleaf) (void)fprintf(tracefp, "%.0f%% leaf fill (%ld bytes used, %ld bytes free)\n", ((double)(pleaf - lfree) / pleaf) * 100, pleaf - lfree, lfree); pinternal *= t->bt_psize - BTDATAOFF; if (pinternal) (void)fprintf(tracefp, "%.0f%% internal fill (%ld bytes used, %ld bytes free\n", ((double)(pinternal - ifree) / pinternal) * 100, pinternal - ifree, ifree); if (bt_pfxsaved) (void)fprintf(tracefp, "prefix checking removed %lu bytes.\n", bt_pfxsaved); (void)fflush(tracefp); return (0); } #endif /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_delete.c 8.13 (Berkeley) 7/28/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include "db-int.h" #include "btree.h" static int __bt_bdelete __P((BTREE *, const DBT *)); static int __bt_curdel __P((BTREE *, const DBT *, PAGE *, u_int)); static int __bt_pdelete __P((BTREE *, PAGE *)); static int __bt_relink __P((BTREE *, PAGE *)); static int __bt_stkacq __P((BTREE *, PAGE **, CURSOR *)); /* * __bt_delete * Delete the item(s) referenced by a key. * * Return RET_SPECIAL if the key is not found. */ int __bt_delete(dbp, key, flags) const DB *dbp; const DBT *key; u_int flags; { BTREE *t; CURSOR *c; PAGE *h; int status; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } /* Check for change to a read-only tree. */ if (F_ISSET(t, B_RDONLY)) { errno = EPERM; return (RET_ERROR); } switch (flags) { case 0: status = __bt_bdelete(t, key); break; case R_CURSOR: /* * If flags is R_CURSOR, delete the cursor. Must already * have started a scan and not have already deleted it. */ c = &t->bt_cursor; if (F_ISSET(c, CURS_INIT)) { if (F_ISSET(c, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE)) return (RET_SPECIAL); if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL) return (RET_ERROR); /* * If the page is about to be emptied, we'll need to * delete it, which means we have to acquire a stack. */ if (NEXTINDEX(h) == 1) if (__bt_stkacq(t, &h, &t->bt_cursor)) return (RET_ERROR); status = __bt_dleaf(t, NULL, h, c->pg.index); if (NEXTINDEX(h) == 0 && status == RET_SUCCESS) { if (__bt_pdelete(t, h)) return (RET_ERROR); } else mpool_put(t->bt_mp, h, status == RET_SUCCESS ? MPOOL_DIRTY : 0); break; } /* FALLTHROUGH */ default: errno = EINVAL; return (RET_ERROR); } if (status == RET_SUCCESS) F_SET(t, B_MODIFIED); return (status); } /* * __bt_stkacq -- * Acquire a stack so we can delete a cursor entry. * * Parameters: * t: tree * hp: pointer to current, pinned PAGE pointer * c: pointer to the cursor * * Returns: * 0 on success, 1 on failure */ static int __bt_stkacq(t, hp, c) BTREE *t; PAGE **hp; CURSOR *c; { BINTERNAL *bi; EPG *e; EPGNO *parent; PAGE *h; indx_t idx; db_pgno_t pgno; recno_t nextpg, prevpg; int exact, level; /* * Find the first occurrence of the key in the tree. Toss the * currently locked page so we don't hit an already-locked page. */ h = *hp; mpool_put(t->bt_mp, h, 0); if ((e = __bt_search(t, &c->key, &exact)) == NULL) return (1); h = e->page; /* See if we got it in one shot. */ if (h->pgno == c->pg.pgno) goto ret; /* * Move right, looking for the page. At each move we have to move * up the stack until we don't have to move to the next page. If * we have to change pages at an internal level, we have to fix the * stack back up. */ while (h->pgno != c->pg.pgno) { if ((nextpg = h->nextpg) == P_INVALID) break; mpool_put(t->bt_mp, h, 0); /* Move up the stack. */ for (level = 0; (parent = BT_POP(t)) != NULL; ++level) { /* Get the parent page. */ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) return (1); /* Move to the next index. */ if (parent->index != NEXTINDEX(h) - 1) { idx = parent->index + 1; BT_PUSH(t, h->pgno, idx); break; } mpool_put(t->bt_mp, h, 0); } /* Restore the stack. */ while (level--) { /* Push the next level down onto the stack. */ bi = GETBINTERNAL(h, idx); pgno = bi->pgno; BT_PUSH(t, pgno, 0); /* Lose the currently pinned page. */ mpool_put(t->bt_mp, h, 0); /* Get the next level down. */ if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL) return (1); idx = 0; } mpool_put(t->bt_mp, h, 0); if ((h = mpool_get(t->bt_mp, nextpg, 0)) == NULL) return (1); } if (h->pgno == c->pg.pgno) goto ret; /* Reacquire the original stack. */ mpool_put(t->bt_mp, h, 0); if ((e = __bt_search(t, &c->key, &exact)) == NULL) return (1); h = e->page; /* * Move left, looking for the page. At each move we have to move * up the stack until we don't have to change pages to move to the * next page. If we have to change pages at an internal level, we * have to fix the stack back up. */ while (h->pgno != c->pg.pgno) { if ((prevpg = h->prevpg) == P_INVALID) break; mpool_put(t->bt_mp, h, 0); /* Move up the stack. */ for (level = 0; (parent = BT_POP(t)) != NULL; ++level) { /* Get the parent page. */ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) return (1); /* Move to the next index. */ if (parent->index != 0) { idx = parent->index - 1; BT_PUSH(t, h->pgno, idx); break; } mpool_put(t->bt_mp, h, 0); } /* Restore the stack. */ while (level--) { /* Push the next level down onto the stack. */ bi = GETBINTERNAL(h, idx); pgno = bi->pgno; /* Lose the currently pinned page. */ mpool_put(t->bt_mp, h, 0); /* Get the next level down. */ if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL) return (1); idx = NEXTINDEX(h) - 1; BT_PUSH(t, pgno, idx); } mpool_put(t->bt_mp, h, 0); if ((h = mpool_get(t->bt_mp, prevpg, 0)) == NULL) return (1); } ret: mpool_put(t->bt_mp, h, 0); return ((*hp = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL); } /* * __bt_bdelete -- * Delete all key/data pairs matching the specified key. * * Parameters: * t: tree * key: key to delete * * Returns: * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. */ static int __bt_bdelete(t, key) BTREE *t; const DBT *key; { EPG *e; PAGE *h; int deleted, exact, redo; deleted = 0; /* Find any matching record; __bt_search pins the page. */ loop: if ((e = __bt_search(t, key, &exact)) == NULL) return (deleted ? RET_SUCCESS : RET_ERROR); if (!exact) { mpool_put(t->bt_mp, e->page, 0); return (deleted ? RET_SUCCESS : RET_SPECIAL); } /* * Delete forward, then delete backward, from the found key. If * there are duplicates and we reach either side of the page, do * the key search again, so that we get them all. */ redo = 0; h = e->page; do { if (__bt_dleaf(t, key, h, e->index)) { mpool_put(t->bt_mp, h, 0); return (RET_ERROR); } if (F_ISSET(t, B_NODUPS)) { if (NEXTINDEX(h) == 0) { if (__bt_pdelete(t, h)) return (RET_ERROR); } else mpool_put(t->bt_mp, h, MPOOL_DIRTY); return (RET_SUCCESS); } deleted = 1; } while (e->index < NEXTINDEX(h) && __bt_cmp(t, key, e) == 0); /* Check for right-hand edge of the page. */ if (e->index == NEXTINDEX(h)) redo = 1; /* Delete from the key to the beginning of the page. */ while (e->index-- > 0) { if (__bt_cmp(t, key, e) != 0) break; if (__bt_dleaf(t, key, h, e->index) == RET_ERROR) { mpool_put(t->bt_mp, h, 0); return (RET_ERROR); } if (e->index == 0) redo = 1; } /* Check for an empty page. */ if (NEXTINDEX(h) == 0) { if (__bt_pdelete(t, h)) return (RET_ERROR); goto loop; } /* Put the page. */ mpool_put(t->bt_mp, h, MPOOL_DIRTY); if (redo) goto loop; return (RET_SUCCESS); } /* * __bt_pdelete -- * Delete a single page from the tree. * * Parameters: * t: tree * h: leaf page * * Returns: * RET_SUCCESS, RET_ERROR. * * Side-effects: * mpool_put's the page */ static int __bt_pdelete(t, h) BTREE *t; PAGE *h; { BINTERNAL *bi; PAGE *pg; EPGNO *parent; indx_t cnt, idx, *ip, offset; u_int32_t nksize; char *from; /* * Walk the parent page stack -- a LIFO stack of the pages that were * traversed when we searched for the page where the delete occurred. * Each stack entry is a page number and a page index offset. The * offset is for the page traversed on the search. We've just deleted * a page, so we have to delete the key from the parent page. * * If the delete from the parent page makes it empty, this process may * continue all the way up the tree. We stop if we reach the root page * (which is never deleted, it's just not worth the effort) or if the * delete does not empty the page. */ while ((parent = BT_POP(t)) != NULL) { /* Get the parent page. */ if ((pg = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) return (RET_ERROR); idx = parent->index; bi = GETBINTERNAL(pg, idx); /* Free any overflow pages. */ if (bi->flags & P_BIGKEY && __ovfl_delete(t, bi->bytes) == RET_ERROR) { mpool_put(t->bt_mp, pg, 0); return (RET_ERROR); } /* * Free the parent if it has only the one key and it's not the * root page. If it's the rootpage, turn it back into an empty * leaf page. */ if (NEXTINDEX(pg) == 1) if (pg->pgno == P_ROOT) { pg->lower = BTDATAOFF; pg->upper = t->bt_psize; pg->flags = P_BLEAF; } else { if (__bt_relink(t, pg) || __bt_free(t, pg)) return (RET_ERROR); continue; } else { /* Pack remaining key items at the end of the page. */ nksize = NBINTERNAL(bi->ksize); from = (char *)pg + pg->upper; memmove(from + nksize, from, (char *)bi - from); pg->upper += nksize; /* Adjust indices' offsets, shift the indices down. */ offset = pg->linp[idx]; for (cnt = idx, ip = &pg->linp[0]; cnt--; ++ip) if (ip[0] < offset) ip[0] += nksize; for (cnt = NEXTINDEX(pg) - idx; --cnt; ++ip) ip[0] = ip[1] < offset ? ip[1] + nksize : ip[1]; pg->lower -= sizeof(indx_t); } mpool_put(t->bt_mp, pg, MPOOL_DIRTY); break; } /* Free the leaf page, as long as it wasn't the root. */ if (h->pgno == P_ROOT) { mpool_put(t->bt_mp, h, MPOOL_DIRTY); return (RET_SUCCESS); } return (__bt_relink(t, h) || __bt_free(t, h)); } /* * __bt_dleaf -- * Delete a single record from a leaf page. * * Parameters: * t: tree * key: referenced key * h: page * idx: index on page to delete * * Returns: * RET_SUCCESS, RET_ERROR. */ int __bt_dleaf(t, key, h, idx) BTREE *t; const DBT *key; PAGE *h; u_int idx; { BLEAF *bl; indx_t cnt, *ip, offset; u_int32_t nbytes; void *to; char *from; /* If this record is referenced by the cursor, delete the cursor. */ if (F_ISSET(&t->bt_cursor, CURS_INIT) && !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) && t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index == idx && __bt_curdel(t, key, h, idx)) return (RET_ERROR); /* If the entry uses overflow pages, make them available for reuse. */ to = bl = GETBLEAF(h, idx); if (bl->flags & P_BIGKEY && __ovfl_delete(t, bl->bytes) == RET_ERROR) return (RET_ERROR); if (bl->flags & P_BIGDATA && __ovfl_delete(t, bl->bytes + bl->ksize) == RET_ERROR) return (RET_ERROR); /* Pack the remaining key/data items at the end of the page. */ nbytes = NBLEAF(bl); from = (char *)h + h->upper; memmove(from + nbytes, from, (char *)to - from); h->upper += nbytes; /* Adjust the indices' offsets, shift the indices down. */ offset = h->linp[idx]; for (cnt = idx, ip = &h->linp[0]; cnt--; ++ip) if (ip[0] < offset) ip[0] += nbytes; for (cnt = NEXTINDEX(h) - idx; --cnt; ++ip) ip[0] = ip[1] < offset ? ip[1] + nbytes : ip[1]; h->lower -= sizeof(indx_t); /* If the cursor is on this page, adjust it as necessary. */ if (F_ISSET(&t->bt_cursor, CURS_INIT) && !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) && t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index > idx) --t->bt_cursor.pg.index; return (RET_SUCCESS); } /* * __bt_curdel -- * Delete the cursor. * * Parameters: * t: tree * key: referenced key (or NULL) * h: page * idx: idx on page to delete * * Returns: * RET_SUCCESS, RET_ERROR. */ static int __bt_curdel(t, key, h, idx) BTREE *t; const DBT *key; PAGE *h; u_int idx; { CURSOR *c; EPG e; PAGE *pg; int curcopy, status; /* * If there are duplicates, move forward or backward to one. * Otherwise, copy the key into the cursor area. */ c = &t->bt_cursor; F_CLR(c, CURS_AFTER | CURS_BEFORE | CURS_ACQUIRE); curcopy = 0; if (!F_ISSET(t, B_NODUPS)) { /* * We're going to have to do comparisons. If we weren't * provided a copy of the key, i.e. the user is deleting * the current cursor position, get one. */ if (key == NULL) { e.page = h; e.index = idx; if ((status = __bt_ret(t, &e, &c->key, &c->key, NULL, NULL, 1)) != RET_SUCCESS) return (status); curcopy = 1; key = &c->key; } /* Check previous key, if not at the beginning of the page. */ if (idx > 0) { e.page = h; e.index = idx - 1; if (__bt_cmp(t, key, &e) == 0) { F_SET(c, CURS_BEFORE); goto dup2; } } /* Check next key, if not at the end of the page. */ if (idx < NEXTINDEX(h) - 1) { e.page = h; e.index = idx + 1; if (__bt_cmp(t, key, &e) == 0) { F_SET(c, CURS_AFTER); goto dup2; } } /* Check previous key if at the beginning of the page. */ if (idx == 0 && h->prevpg != P_INVALID) { if ((pg = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL) return (RET_ERROR); e.page = pg; e.index = NEXTINDEX(pg) - 1; if (__bt_cmp(t, key, &e) == 0) { F_SET(c, CURS_BEFORE); goto dup1; } mpool_put(t->bt_mp, pg, 0); } /* Check next key if at the end of the page. */ if (idx == NEXTINDEX(h) - 1 && h->nextpg != P_INVALID) { if ((pg = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL) return (RET_ERROR); e.page = pg; e.index = 0; if (__bt_cmp(t, key, &e) == 0) { F_SET(c, CURS_AFTER); dup1: mpool_put(t->bt_mp, pg, 0); dup2: c->pg.pgno = e.page->pgno; c->pg.index = e.index; return (RET_SUCCESS); } mpool_put(t->bt_mp, pg, 0); } } e.page = h; e.index = idx; if (curcopy || (status = __bt_ret(t, &e, &c->key, &c->key, NULL, NULL, 1)) == RET_SUCCESS) { F_SET(c, CURS_ACQUIRE); return (RET_SUCCESS); } return (status); } /* * __bt_relink -- * Link around a deleted page. * * Parameters: * t: tree * h: page to be deleted */ static int __bt_relink(t, h) BTREE *t; PAGE *h; { PAGE *pg; if (h->nextpg != P_INVALID) { if ((pg = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL) return (RET_ERROR); pg->prevpg = h->prevpg; mpool_put(t->bt_mp, pg, MPOOL_DIRTY); } if (h->prevpg != P_INVALID) { if ((pg = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL) return (RET_ERROR); pg->nextpg = h->nextpg; mpool_put(t->bt_mp, pg, MPOOL_DIRTY); } return (0); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_get.c 8.6 (Berkeley) 7/20/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include "db-int.h" #include "btree.h" /* * __BT_GET -- Get a record from the btree. * * Parameters: * dbp: pointer to access method * key: key to find * data: data to return * flag: currently unused * * Returns: * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. */ int __bt_get(dbp, key, data, flags) const DB *dbp; const DBT *key; DBT *data; u_int flags; { BTREE *t; EPG *e; int exact, status; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } /* Get currently doesn't take any flags. */ if (flags) { errno = EINVAL; return (RET_ERROR); } if ((e = __bt_search(t, key, &exact)) == NULL) return (RET_ERROR); if (!exact) { mpool_put(t->bt_mp, e->page, 0); return (RET_SPECIAL); } status = __bt_ret(t, e, NULL, NULL, data, &t->bt_rdata, 0); /* * If the user is doing concurrent access, we copied the * key/data, toss the page. */ if (F_ISSET(t, B_DB_LOCK)) mpool_put(t->bt_mp, e->page, 0); else t->bt_pinned = e->page; return (status); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_open.c 8.11 (Berkeley) 11/2/95"; #endif /* LIBC_SCCS and not lint */ /* * Implementation of btree access method for 4.4BSD. * * The design here was originally based on that of the btree access method * used in the Postgres database system at UC Berkeley. This implementation * is wholly independent of the Postgres code. */ #include #include #include #include #include #include #include #include #include #include #include "db-int.h" #include "btree.h" #ifdef DEBUG #undef MINPSIZE #define MINPSIZE 128 #endif static int byteorder __P((void)); static int nroot __P((BTREE *)); static int tmp __P((void)); /* * __BT_OPEN -- Open a btree. * * Creates and fills a DB struct, and calls the routine that actually * opens the btree. * * Parameters: * fname: filename (NULL for in-memory trees) * flags: open flag bits * mode: open permission bits * b: BTREEINFO pointer * * Returns: * NULL on failure, pointer to DB on success. * */ DB * __bt_open(fname, flags, mode, openinfo, dflags) const char *fname; int flags, mode, dflags; const BTREEINFO *openinfo; { struct stat sb; BTMETA m; BTREE *t; BTREEINFO b; DB *dbp; db_pgno_t ncache; ssize_t nr; int machine_lorder; t = NULL; /* * Intention is to make sure all of the user's selections are okay * here and then use them without checking. Can't be complete, since * we don't know the right page size, lorder or flags until the backing * file is opened. Also, the file's page size can cause the cachesize * to change. */ machine_lorder = byteorder(); if (openinfo) { b = *openinfo; /* Flags: R_DUP. */ if (b.flags & ~(R_DUP)) goto einval; /* * Page size must be indx_t aligned and >= MINPSIZE. Default * page size is set farther on, based on the underlying file * transfer size. */ if (b.psize && (b.psize < MINPSIZE || b.psize > MAX_PAGE_OFFSET + 1 || b.psize & (sizeof(indx_t) - 1))) goto einval; /* Minimum number of keys per page; absolute minimum is 2. */ if (b.minkeypage) { if (b.minkeypage < 2) goto einval; } else b.minkeypage = DEFMINKEYPAGE; /* If no comparison, use default comparison and prefix. */ if (b.compare == NULL) { b.compare = __bt_defcmp; if (b.prefix == NULL) b.prefix = __bt_defpfx; } if (b.lorder == 0) b.lorder = machine_lorder; } else { b.compare = __bt_defcmp; b.cachesize = 0; b.flags = 0; b.lorder = machine_lorder; b.minkeypage = DEFMINKEYPAGE; b.prefix = __bt_defpfx; b.psize = 0; } /* Check for the ubiquitous PDP-11. */ if (b.lorder != DB_BIG_ENDIAN && b.lorder != DB_LITTLE_ENDIAN) goto einval; /* Allocate and initialize DB and BTREE structures. */ if ((t = (BTREE *)malloc(sizeof(BTREE))) == NULL) goto err; memset(t, 0, sizeof(BTREE)); t->bt_fd = -1; /* Don't close unopened fd on error. */ t->bt_lorder = b.lorder; t->bt_order = NOT; t->bt_cmp = b.compare; t->bt_pfx = b.prefix; t->bt_rfd = -1; if ((t->bt_dbp = dbp = (DB *)malloc(sizeof(DB))) == NULL) goto err; memset(t->bt_dbp, 0, sizeof(DB)); if (t->bt_lorder != machine_lorder) F_SET(t, B_NEEDSWAP); dbp->type = DB_BTREE; dbp->internal = t; dbp->close = __bt_close; dbp->del = __bt_delete; dbp->fd = __bt_fd; dbp->get = __bt_get; dbp->put = __bt_put; dbp->seq = __bt_seq; dbp->sync = __bt_sync; /* * If no file name was supplied, this is an in-memory btree and we * open a backing temporary file. Otherwise, it's a disk-based tree. */ if (fname) { switch (flags & O_ACCMODE) { case O_RDONLY: F_SET(t, B_RDONLY); break; case O_RDWR: break; case O_WRONLY: default: goto einval; } if ((t->bt_fd = open(fname, flags | O_BINARY, mode)) < 0) goto err; } else { if ((flags & O_ACCMODE) != O_RDWR) goto einval; if ((t->bt_fd = tmp()) == -1) goto err; F_SET(t, B_INMEM); } if (fcntl(t->bt_fd, F_SETFD, 1) == -1) goto err; if (fstat(t->bt_fd, &sb)) goto err; if (sb.st_size) { if ((nr = read(t->bt_fd, &m, sizeof(BTMETA))) < 0) goto err; if (nr != sizeof(BTMETA)) goto eftype; /* * Read in the meta-data. This can change the notion of what * the lorder, page size and flags are, and, when the page size * changes, the cachesize value can change too. If the user * specified the wrong byte order for an existing database, we * don't bother to return an error, we just clear the NEEDSWAP * bit. */ if (m.magic == BTREEMAGIC) F_CLR(t, B_NEEDSWAP); else { F_SET(t, B_NEEDSWAP); M_32_SWAP(m.magic); M_32_SWAP(m.version); M_32_SWAP(m.psize); M_32_SWAP(m.free); M_32_SWAP(m.nrecs); M_32_SWAP(m.flags); } if (m.magic != BTREEMAGIC || m.version != BTREEVERSION) goto eftype; if (m.psize < MINPSIZE || m.psize > MAX_PAGE_OFFSET + 1 || m.psize & (sizeof(indx_t) - 1)) goto eftype; if (m.flags & ~SAVEMETA) goto eftype; b.psize = m.psize; F_SET(t, m.flags); t->bt_free = m.free; t->bt_nrecs = m.nrecs; } else { /* * Set the page size to the best value for I/O to this file. * Don't overflow the page offset type. */ if (b.psize == 0) { b.psize = sb.st_blksize; if (b.psize < MINPSIZE) b.psize = MINPSIZE; if (b.psize > MAX_PAGE_OFFSET + 1) b.psize = MAX_PAGE_OFFSET + 1; } /* Set flag if duplicates permitted. */ if (!(b.flags & R_DUP)) F_SET(t, B_NODUPS); t->bt_free = P_INVALID; t->bt_nrecs = 0; F_SET(t, B_METADIRTY); } t->bt_psize = b.psize; /* Set the cache size; must be a multiple of the page size. */ if (b.cachesize && b.cachesize & (b.psize - 1)) b.cachesize += (~b.cachesize & (b.psize - 1)) + 1; if (b.cachesize < b.psize * MINCACHE) b.cachesize = b.psize * MINCACHE; /* Calculate number of pages to cache. */ ncache = (b.cachesize + t->bt_psize - 1) / t->bt_psize; /* * The btree data structure requires that at least two keys can fit on * a page, but other than that there's no fixed requirement. The user * specified a minimum number per page, and we translated that into the * number of bytes a key/data pair can use before being placed on an * overflow page. This calculation includes the page header, the size * of the index referencing the leaf item and the size of the leaf item * structure. Also, don't let the user specify a minkeypage such that * a key/data pair won't fit even if both key and data are on overflow * pages. */ t->bt_ovflsize = (t->bt_psize - BTDATAOFF) / b.minkeypage - (sizeof(indx_t) + NBLEAFDBT(0, 0)); if (t->bt_ovflsize < NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t)) t->bt_ovflsize = NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t); /* Initialize the buffer pool. */ if ((t->bt_mp = mpool_open(NULL, t->bt_fd, t->bt_psize, ncache)) == NULL) goto err; if (!F_ISSET(t, B_INMEM)) mpool_filter(t->bt_mp, __bt_pgin, __bt_pgout, t); /* Create a root page if new tree. */ if (nroot(t) == RET_ERROR) goto err; /* Global flags. */ if (dflags & DB_LOCK) F_SET(t, B_DB_LOCK); if (dflags & DB_SHMEM) F_SET(t, B_DB_SHMEM); if (dflags & DB_TXN) F_SET(t, B_DB_TXN); return (dbp); einval: errno = EINVAL; goto err; eftype: errno = EFTYPE; goto err; err: if (t) { if (t->bt_dbp) free(t->bt_dbp); if (t->bt_fd != -1) (void)close(t->bt_fd); free(t); } return (NULL); } /* * NROOT -- Create the root of a new tree. * * Parameters: * t: tree * * Returns: * RET_ERROR, RET_SUCCESS */ static int nroot(t) BTREE *t; { PAGE *meta, *root; db_pgno_t npg; if ((root = mpool_get(t->bt_mp, 1, 0)) != NULL) { if (root->lower == 0 && root->pgno == 0 && root->linp[0] == 0) { mpool_delete(t->bt_mp, root); errno = EINVAL; } else { mpool_put(t->bt_mp, root, 0); return (RET_SUCCESS); } } if (errno != EINVAL) /* It's OK to not exist. */ return (RET_ERROR); errno = 0; if ((meta = mpool_new(t->bt_mp, &npg, MPOOL_PAGE_NEXT)) == NULL) return (RET_ERROR); if ((root = mpool_new(t->bt_mp, &npg, MPOOL_PAGE_NEXT)) == NULL) return (RET_ERROR); if (npg != P_ROOT) return (RET_ERROR); root->pgno = npg; root->prevpg = root->nextpg = P_INVALID; root->lower = BTDATAOFF; root->upper = t->bt_psize; root->flags = P_BLEAF; memset(meta, 0, t->bt_psize); mpool_put(t->bt_mp, meta, MPOOL_DIRTY); mpool_put(t->bt_mp, root, MPOOL_DIRTY); return (RET_SUCCESS); } static int tmp() { #ifdef SIG_BLOCK sigset_t set, oset; #else int oset; #endif int fd; char *envtmp; char path[MAXPATHLEN]; static char fn[] = "/bt.XXXXXX"; envtmp = getenv("TMPDIR"); /* this used to be done with snprintf(), but since snprintf isn't in most operating systems, and overflow checking in this case is easy, this is what is done */ if (envtmp && ((strlen(envtmp)+sizeof(fn)+1) > sizeof(path))) return(-1); (void)sprintf(path, "%s%s", (envtmp ? envtmp : "/tmp"), fn); #ifdef SIG_BLOCK (void)sigfillset(&set); (void)sigprocmask(SIG_BLOCK, &set, &oset); #else oset = sigblock(~0); #endif if ((fd = mkstemp(path)) != -1) (void)unlink(path); #ifdef SIG_BLOCK (void)sigprocmask(SIG_SETMASK, &oset, NULL); #else sigsetmask(oset); #endif #ifdef __CYGWIN32__ /* Ensure the fd is in binary mode. */ setmode(fd, O_BINARY); #endif /* __CYGWIN32__ */ return(fd); } static int byteorder() { u_int32_t x; u_char *p; x = 0x01020304; p = (u_char *)&x; switch (*p) { case 1: return (DB_BIG_ENDIAN); case 4: return (DB_LITTLE_ENDIAN); default: return (0); } } int __bt_fd(dbp) const DB *dbp; { BTREE *t; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } /* In-memory database can't have a file descriptor. */ if (F_ISSET(t, B_INMEM)) { errno = ENOENT; return (-1); } return (t->bt_fd); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_overflow.c 8.5 (Berkeley) 7/16/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include "db-int.h" #include "btree.h" /* * Big key/data code. * * Big key and data entries are stored on linked lists of pages. The initial * reference is byte string stored with the key or data and is the page number * and size. The actual record is stored in a chain of pages linked by the * nextpg field of the PAGE header. * * The first page of the chain has a special property. If the record is used * by an internal page, it cannot be deleted and the P_PRESERVE bit will be set * in the header. * * XXX * A single DBT is written to each chain, so a lot of space on the last page * is wasted. This is a fairly major bug for some data sets. */ /* * __OVFL_GET -- Get an overflow key/data item. * * Parameters: * t: tree * p: pointer to { db_pgno_t, u_int32_t } * buf: storage address * bufsz: storage size * * Returns: * RET_ERROR, RET_SUCCESS */ int __ovfl_get(t, p, ssz, buf, bufsz) BTREE *t; void *p; size_t *ssz; void **buf; size_t *bufsz; { PAGE *h; db_pgno_t pg; size_t nb, plen; u_int32_t sz; memmove(&pg, p, sizeof(db_pgno_t)); memmove(&sz, (char *)p + sizeof(db_pgno_t), sizeof(u_int32_t)); *ssz = sz; #ifdef DEBUG if (pg == P_INVALID || sz == 0) abort(); #endif /* Make the buffer bigger as necessary. */ if (*bufsz < sz) { *buf = (char *)(*buf == NULL ? malloc(sz) : realloc(*buf, sz)); if (*buf == NULL) return (RET_ERROR); *bufsz = sz; } /* * Step through the linked list of pages, copying the data on each one * into the buffer. Never copy more than the data's length. */ plen = t->bt_psize - BTDATAOFF; for (p = *buf;; p = (char *)p + nb, pg = h->nextpg) { if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); nb = MIN(sz, plen); memmove(p, (char *)h + BTDATAOFF, nb); mpool_put(t->bt_mp, h, 0); if ((sz -= nb) == 0) break; } return (RET_SUCCESS); } /* * __OVFL_PUT -- Store an overflow key/data item. * * Parameters: * t: tree * data: DBT to store * pgno: storage page number * * Returns: * RET_ERROR, RET_SUCCESS */ int __ovfl_put(t, dbt, pg) BTREE *t; const DBT *dbt; db_pgno_t *pg; { PAGE *h, *last; void *p; db_pgno_t npg; size_t nb, plen; u_int32_t sz; /* * Allocate pages and copy the key/data record into them. Store the * number of the first page in the chain. */ plen = t->bt_psize - BTDATAOFF; for (last = NULL, p = dbt->data, sz = dbt->size;; p = (char *)p + plen, last = h) { if ((h = __bt_new(t, &npg)) == NULL) return (RET_ERROR); h->pgno = npg; h->nextpg = h->prevpg = P_INVALID; h->flags = P_OVERFLOW; h->lower = h->upper = 0; nb = MIN(sz, plen); memmove((char *)h + BTDATAOFF, p, nb); if (last) { last->nextpg = h->pgno; mpool_put(t->bt_mp, last, MPOOL_DIRTY); } else *pg = h->pgno; if ((sz -= nb) == 0) { mpool_put(t->bt_mp, h, MPOOL_DIRTY); break; } } return (RET_SUCCESS); } /* * __OVFL_DELETE -- Delete an overflow chain. * * Parameters: * t: tree * p: pointer to { db_pgno_t, u_int32_t } * * Returns: * RET_ERROR, RET_SUCCESS */ int __ovfl_delete(t, p) BTREE *t; void *p; { PAGE *h; db_pgno_t pg; size_t plen; u_int32_t sz; memmove(&pg, p, sizeof(db_pgno_t)); memmove(&sz, (char *)p + sizeof(db_pgno_t), sizeof(u_int32_t)); #ifdef DEBUG if (pg == P_INVALID || sz == 0) abort(); #endif if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); /* Don't delete chains used by internal pages. */ if (h->flags & P_PRESERVE) { mpool_put(t->bt_mp, h, 0); return (RET_SUCCESS); } /* Step through the chain, calling the free routine for each page. */ for (plen = t->bt_psize - BTDATAOFF;; sz -= plen) { pg = h->nextpg; __bt_free(t, h); if (sz <= plen) break; if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); } return (RET_SUCCESS); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_page.c 8.4 (Berkeley) 11/2/95"; #endif /* LIBC_SCCS and not lint */ #include #include #include "db-int.h" #include "btree.h" /* * __bt_free -- * Put a page on the freelist. * * Parameters: * t: tree * h: page to free * * Returns: * RET_ERROR, RET_SUCCESS * * Side-effect: * mpool_put's the page. */ int __bt_free(t, h) BTREE *t; PAGE *h; { /* Insert the page at the head of the free list. */ h->prevpg = P_INVALID; h->nextpg = t->bt_free; t->bt_free = h->pgno; F_SET(t, B_METADIRTY); /* Make sure the page gets written back. */ return (mpool_put(t->bt_mp, h, MPOOL_DIRTY)); } /* * __bt_new -- * Get a new page, preferably from the freelist. * * Parameters: * t: tree * npg: storage for page number. * * Returns: * Pointer to a page, NULL on error. */ PAGE * __bt_new(t, npg) BTREE *t; db_pgno_t *npg; { PAGE *h; if (t->bt_free != P_INVALID && (h = mpool_get(t->bt_mp, t->bt_free, 0)) != NULL) { *npg = t->bt_free; t->bt_free = h->nextpg; F_SET(t, B_METADIRTY); return (h); } return (mpool_new(t->bt_mp, npg, MPOOL_PAGE_NEXT)); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_put.c 8.8 (Berkeley) 7/26/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include "db-int.h" #include "btree.h" static EPG *bt_fast __P((BTREE *, const DBT *, const DBT *, int *)); /* * __BT_PUT -- Add a btree item to the tree. * * Parameters: * dbp: pointer to access method * key: key * data: data * flag: R_NOOVERWRITE * * Returns: * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key is already in the * tree and R_NOOVERWRITE specified. */ int __bt_put(dbp, key, data, flags) const DB *dbp; DBT *key; const DBT *data; u_int flags; { BTREE *t; DBT tkey, tdata; EPG *e = 0; PAGE *h; indx_t idx, nxtindex; db_pgno_t pg; u_int32_t nbytes; int dflags, exact, status; char *dest, db[NOVFLSIZE], kb[NOVFLSIZE]; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } /* Check for change to a read-only tree. */ if (F_ISSET(t, B_RDONLY)) { errno = EPERM; return (RET_ERROR); } switch (flags) { case 0: case R_NOOVERWRITE: break; case R_CURSOR: /* * If flags is R_CURSOR, put the cursor. Must already * have started a scan and not have already deleted it. */ if (F_ISSET(&t->bt_cursor, CURS_INIT) && !F_ISSET(&t->bt_cursor, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE)) break; /* FALLTHROUGH */ default: errno = EINVAL; return (RET_ERROR); } /* * If the key/data pair won't fit on a page, store it on overflow * pages. Only put the key on the overflow page if the pair are * still too big after moving the data to an overflow page. * * XXX * If the insert fails later on, the overflow pages aren't recovered. */ dflags = 0; if (key->size + data->size > t->bt_ovflsize) { if (key->size > t->bt_ovflsize) { u_int32_t yuck_this_is_gross_code; storekey: if (__ovfl_put(t, key, &pg) == RET_ERROR) return (RET_ERROR); tkey.data = kb; tkey.size = NOVFLSIZE; memmove(kb, &pg, sizeof(db_pgno_t)); yuck_this_is_gross_code = key->size; if (yuck_this_is_gross_code != key->size) abort (); memmove(kb + sizeof(db_pgno_t), &yuck_this_is_gross_code, sizeof(u_int32_t)); dflags |= P_BIGKEY; key = &tkey; } if (key->size + data->size > t->bt_ovflsize) { u_int32_t yuck_this_is_gross_code = data->size; if (__ovfl_put(t, data, &pg) == RET_ERROR) return (RET_ERROR); tdata.data = db; tdata.size = NOVFLSIZE; memmove(db, &pg, sizeof(db_pgno_t)); if (yuck_this_is_gross_code != data->size) abort (); memmove(db + sizeof(db_pgno_t), &yuck_this_is_gross_code, sizeof(u_int32_t)); dflags |= P_BIGDATA; data = &tdata; } if (key->size + data->size > t->bt_ovflsize) goto storekey; } /* Replace the cursor. */ if (flags == R_CURSOR) { if ((h = mpool_get(t->bt_mp, t->bt_cursor.pg.pgno, 0)) == NULL) return (RET_ERROR); idx = t->bt_cursor.pg.index; goto delete; } /* * Find the key to delete, or, the location at which to insert. * Bt_fast and __bt_search both pin the returned page. */ if (t->bt_order == NOT || (e = bt_fast(t, key, data, &exact)) == NULL) if ((e = __bt_search(t, key, &exact)) == NULL) return (RET_ERROR); h = e->page; idx = e->index; /* * Add the key/data pair to the tree. If an identical key is already * in the tree, and R_NOOVERWRITE is set, an error is returned. If * R_NOOVERWRITE is not set, the key is either added (if duplicates are * permitted) or an error is returned. */ switch (flags) { case R_NOOVERWRITE: if (!exact) break; mpool_put(t->bt_mp, h, 0); return (RET_SPECIAL); default: if (!exact || !F_ISSET(t, B_NODUPS)) break; /* * !!! * Note, the delete may empty the page, so we need to put a * new entry into the page immediately. */ delete: if (__bt_dleaf(t, key, h, idx) == RET_ERROR) { mpool_put(t->bt_mp, h, 0); return (RET_ERROR); } break; } /* * If not enough room, or the user has put a ceiling on the number of * keys permitted in the page, split the page. The split code will * insert the key and data and unpin the current page. If inserting * into the offset array, shift the pointers up. */ nbytes = NBLEAFDBT(key->size, data->size); if (h->upper - h->lower < nbytes + sizeof(indx_t)) { if ((status = __bt_split(t, h, key, data, dflags, nbytes, idx)) != RET_SUCCESS) return (status); goto success; } if (idx < (nxtindex = NEXTINDEX(h))) memmove(h->linp + idx + 1, h->linp + idx, (nxtindex - idx) * sizeof(indx_t)); h->lower += sizeof(indx_t); h->linp[idx] = h->upper -= nbytes; dest = (char *)h + h->upper; WR_BLEAF(dest, key, data, dflags); /* If the cursor is on this page, adjust it as necessary. */ if (F_ISSET(&t->bt_cursor, CURS_INIT) && !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) && t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index >= idx) ++t->bt_cursor.pg.index; if (t->bt_order == NOT) { if (h->nextpg == P_INVALID) { if (idx == NEXTINDEX(h) - 1) { t->bt_order = FORWARD; t->bt_last.index = idx; t->bt_last.pgno = h->pgno; } } else if (h->prevpg == P_INVALID) { if (idx == 0) { t->bt_order = BACK; t->bt_last.index = 0; t->bt_last.pgno = h->pgno; } } } mpool_put(t->bt_mp, h, MPOOL_DIRTY); success: if (flags == R_SETCURSOR) __bt_setcur(t, e->page->pgno, e->index); F_SET(t, B_MODIFIED); return (RET_SUCCESS); } #ifdef STATISTICS u_long bt_cache_hit, bt_cache_miss; #endif /* * BT_FAST -- Do a quick check for sorted data. * * Parameters: * t: tree * key: key to insert * * Returns: * EPG for new record or NULL if not found. */ static EPG * bt_fast(t, key, data, exactp) BTREE *t; const DBT *key, *data; int *exactp; { PAGE *h; u_int32_t nbytes; int cmp; if ((h = mpool_get(t->bt_mp, t->bt_last.pgno, 0)) == NULL) { t->bt_order = NOT; return (NULL); } t->bt_cur.page = h; t->bt_cur.index = t->bt_last.index; /* * If won't fit in this page or have too many keys in this page, * have to search to get split stack. */ nbytes = NBLEAFDBT(key->size, data->size); if (h->upper - h->lower < nbytes + sizeof(indx_t)) goto miss; if (t->bt_order == FORWARD) { if (t->bt_cur.page->nextpg != P_INVALID) goto miss; if (t->bt_cur.index != NEXTINDEX(h) - 1) goto miss; if ((cmp = __bt_cmp(t, key, &t->bt_cur)) < 0) goto miss; t->bt_last.index = cmp ? ++t->bt_cur.index : t->bt_cur.index; } else { if (t->bt_cur.page->prevpg != P_INVALID) goto miss; if (t->bt_cur.index != 0) goto miss; if ((cmp = __bt_cmp(t, key, &t->bt_cur)) > 0) goto miss; t->bt_last.index = 0; } *exactp = cmp == 0; #ifdef STATISTICS ++bt_cache_hit; #endif return (&t->bt_cur); miss: #ifdef STATISTICS ++bt_cache_miss; #endif t->bt_order = NOT; mpool_put(t->bt_mp, h, 0); return (NULL); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_search.c 8.9 (Berkeley) 10/26/95"; #endif /* LIBC_SCCS and not lint */ #include #include #include "db-int.h" #include "btree.h" static int __bt_snext __P((BTREE *, PAGE *, const DBT *, int *)); static int __bt_sprev __P((BTREE *, PAGE *, const DBT *, int *)); /* * __bt_search -- * Search a btree for a key. * * Parameters: * t: tree to search * key: key to find * exactp: pointer to exact match flag * * Returns: * The EPG for matching record, if any, or the EPG for the location * of the key, if it were inserted into the tree, is entered into * the bt_cur field of the tree. A pointer to the field is returned. */ EPG * __bt_search(t, key, exactp) BTREE *t; const DBT *key; int *exactp; { PAGE *h; indx_t base, idx, lim; db_pgno_t pg; int cmp; BT_CLR(t); for (pg = P_ROOT;;) { if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (NULL); /* Do a binary search on the current page. */ t->bt_cur.page = h; for (base = 0, lim = NEXTINDEX(h); lim; lim >>= 1) { t->bt_cur.index = idx = base + (lim >> 1); if ((cmp = __bt_cmp(t, key, &t->bt_cur)) == 0) { if (h->flags & P_BLEAF) { *exactp = 1; return (&t->bt_cur); } goto next; } if (cmp > 0) { base = idx + 1; --lim; } } /* * If it's a leaf page, we're almost done. If no duplicates * are allowed, or we have an exact match, we're done. Else, * it's possible that there were matching keys on this page, * which later deleted, and we're on a page with no matches * while there are matches on other pages. If at the start or * end of a page, check the adjacent page. */ if (h->flags & P_BLEAF) { if (!F_ISSET(t, B_NODUPS)) { if (base == 0 && h->prevpg != P_INVALID && __bt_sprev(t, h, key, exactp)) return (&t->bt_cur); if (base == NEXTINDEX(h) && h->nextpg != P_INVALID && __bt_snext(t, h, key, exactp)) return (&t->bt_cur); } *exactp = 0; t->bt_cur.index = base; return (&t->bt_cur); } /* * No match found. Base is the smallest index greater than * key and may be zero or a last + 1 index. If it's non-zero, * decrement by one, and record the internal page which should * be a parent page for the key. If a split later occurs, the * inserted page will be to the right of the saved page. */ idx = base ? base - 1 : base; next: BT_PUSH(t, h->pgno, idx); pg = GETBINTERNAL(h, idx)->pgno; mpool_put(t->bt_mp, h, 0); } } /* * __bt_snext -- * Check for an exact match after the key. * * Parameters: * t: tree * h: current page * key: key * exactp: pointer to exact match flag * * Returns: * If an exact match found. */ static int __bt_snext(t, h, key, exactp) BTREE *t; PAGE *h; const DBT *key; int *exactp; { BINTERNAL *bi; EPG e; EPGNO *parent; indx_t idx; db_pgno_t pgno; int level; /* * Get the next page. The key is either an exact * match, or not as good as the one we already have. */ if ((e.page = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL) return (0); e.index = 0; if (__bt_cmp(t, key, &e) != 0) { mpool_put(t->bt_mp, e.page, 0); return (0); } mpool_put(t->bt_mp, h, 0); t->bt_cur = e; *exactp = 1; /* * Adjust the stack for the movement. * * Move up the stack. */ for (level = 0; (parent = BT_POP(t)) != NULL; ++level) { /* Get the parent page. */ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) return (0); /* Move to the next index. */ if (parent->index != NEXTINDEX(h) - 1) { idx = parent->index + 1; BT_PUSH(t, h->pgno, idx); break; } mpool_put(t->bt_mp, h, 0); } /* Restore the stack. */ while (level--) { /* Push the next level down onto the stack. */ bi = GETBINTERNAL(h, idx); pgno = bi->pgno; BT_PUSH(t, pgno, 0); /* Lose the currently pinned page. */ mpool_put(t->bt_mp, h, 0); /* Get the next level down. */ if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL) return (0); idx = 0; } mpool_put(t->bt_mp, h, 0); return (1); } /* * __bt_sprev -- * Check for an exact match before the key. * * Parameters: * t: tree * h: current page * key: key * exactp: pointer to exact match flag * * Returns: * If an exact match found. */ static int __bt_sprev(t, h, key, exactp) BTREE *t; PAGE *h; const DBT *key; int *exactp; { BINTERNAL *bi; EPG e; EPGNO *parent; indx_t idx; db_pgno_t pgno; int level; /* * Get the previous page. The key is either an exact * match, or not as good as the one we already have. */ if ((e.page = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL) return (0); e.index = NEXTINDEX(e.page) - 1; if (__bt_cmp(t, key, &e) != 0) { mpool_put(t->bt_mp, e.page, 0); return (0); } mpool_put(t->bt_mp, h, 0); t->bt_cur = e; *exactp = 1; /* * Adjust the stack for the movement. * * Move up the stack. */ for (level = 0; (parent = BT_POP(t)) != NULL; ++level) { /* Get the parent page. */ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) return (1); /* Move to the next index. */ if (parent->index != 0) { idx = parent->index - 1; BT_PUSH(t, h->pgno, idx); break; } mpool_put(t->bt_mp, h, 0); } /* Restore the stack. */ while (level--) { /* Push the next level down onto the stack. */ bi = GETBINTERNAL(h, idx); pgno = bi->pgno; /* Lose the currently pinned page. */ mpool_put(t->bt_mp, h, 0); /* Get the next level down. */ if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL) return (1); idx = NEXTINDEX(h) - 1; BT_PUSH(t, pgno, idx); } mpool_put(t->bt_mp, h, 0); return (1); } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (C) 2002 by the Massachusetts Institute of Technology. * All rights reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. */ /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_seq.c 8.9 (Berkeley) 6/20/95"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include #include "db-int.h" #include "btree.h" static int __bt_first __P((BTREE *, const DBT *, EPG *, int *)); static int __bt_seqadv __P((BTREE *, EPG *, int)); static int __bt_seqset __P((BTREE *, EPG *, DBT *, int)); /* * Sequential scan support. * * The tree can be scanned sequentially, starting from either end of the * tree or from any specific key. A scan request before any scanning is * done is initialized as starting from the least node. */ /* * __bt_seq -- * Btree sequential scan interface. * * Parameters: * dbp: pointer to access method * key: key for positioning and return value * data: data return value * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV. * * Returns: * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. */ int __bt_seq(dbp, key, data, flags) const DB *dbp; DBT *key, *data; u_int flags; { BTREE *t; EPG e; int status; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } /* * If scan unitialized as yet, or starting at a specific record, set * the scan to a specific key. Both __bt_seqset and __bt_seqadv pin * the page the cursor references if they're successful. */ switch (flags) { case R_NEXT: case R_PREV: if (F_ISSET(&t->bt_cursor, CURS_INIT)) { status = __bt_seqadv(t, &e, flags); break; } /* FALLTHROUGH */ case R_FIRST: case R_LAST: case R_CURSOR: status = __bt_seqset(t, &e, key, flags); break; default: errno = EINVAL; return (RET_ERROR); } if (status == RET_SUCCESS) { __bt_setcur(t, e.page->pgno, e.index); status = __bt_ret(t, &e, key, &t->bt_rkey, data, &t->bt_rdata, 0); /* * If the user is doing concurrent access, we copied the * key/data, toss the page. */ if (F_ISSET(t, B_DB_LOCK)) mpool_put(t->bt_mp, e.page, 0); else t->bt_pinned = e.page; } return (status); } /* * __bt_seqset -- * Set the sequential scan to a specific key. * * Parameters: * t: tree * ep: storage for returned key * key: key for initial scan position * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV * * Side effects: * Pins the page the cursor references. * * Returns: * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. */ static int __bt_seqset(t, ep, key, flags) BTREE *t; EPG *ep; DBT *key; int flags; { PAGE *h; db_pgno_t pg; int exact; /* * Find the first, last or specific key in the tree and point the * cursor at it. The cursor may not be moved until a new key has * been found. */ switch (flags) { case R_CURSOR: /* Keyed scan. */ /* * Find the first instance of the key or the smallest key * which is greater than or equal to the specified key. */ if (key->data == NULL || key->size == 0) { errno = EINVAL; return (RET_ERROR); } return (__bt_first(t, key, ep, &exact)); case R_FIRST: /* First record. */ case R_NEXT: /* Walk down the left-hand side of the tree. */ for (pg = P_ROOT;;) { if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); /* Check for an empty tree. */ if (NEXTINDEX(h) == 0) { mpool_put(t->bt_mp, h, 0); return (RET_SPECIAL); } if (h->flags & (P_BLEAF | P_RLEAF)) break; pg = GETBINTERNAL(h, 0)->pgno; mpool_put(t->bt_mp, h, 0); } ep->page = h; ep->index = 0; break; case R_LAST: /* Last record. */ case R_PREV: /* Walk down the right-hand side of the tree. */ for (pg = P_ROOT;;) { if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); /* Check for an empty tree. */ if (NEXTINDEX(h) == 0) { mpool_put(t->bt_mp, h, 0); return (RET_SPECIAL); } if (h->flags & (P_BLEAF | P_RLEAF)) break; pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno; mpool_put(t->bt_mp, h, 0); } ep->page = h; ep->index = NEXTINDEX(h) - 1; break; } return (RET_SUCCESS); } /* * __bt_seqadvance -- * Advance the sequential scan. * * Parameters: * t: tree * flags: R_NEXT, R_PREV * * Side effects: * Pins the page the new key/data record is on. * * Returns: * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. */ static int __bt_seqadv(t, ep, flags) BTREE *t; EPG *ep; int flags; { CURSOR *c; PAGE *h; indx_t idx; db_pgno_t pg; int exact, rval; /* * There are a couple of states that we can be in. The cursor has * been initialized by the time we get here, but that's all we know. */ c = &t->bt_cursor; /* * The cursor was deleted and there weren't any duplicate records, * so the cursor's key was saved. Find out where that key would * be in the current tree. If the returned key is an exact match, * it means that a key/data pair was inserted into the tree after * the delete. We could reasonably return the key, but the problem * is that this is the access pattern we'll see if the user is * doing seq(..., R_NEXT)/put(..., 0) pairs, i.e. the put deletes * the cursor record and then replaces it, so the cursor was saved, * and we'll simply return the same "new" record until the user * notices and doesn't do a put() of it. Since the key is an exact * match, we could as easily put the new record before the cursor, * and we've made no guarantee to return it. So, move forward or * back a record if it's an exact match. * * XXX * In the current implementation, put's to the cursor are done with * delete/add pairs. This has two consequences. First, it means * that seq(..., R_NEXT)/put(..., R_CURSOR) pairs are going to exhibit * the same behavior as above. Second, you can return the same key * twice if you have duplicate records. The scenario is that the * cursor record is deleted, moving the cursor forward or backward * to a duplicate. The add then inserts the new record at a location * ahead of the cursor because duplicates aren't sorted in any way, * and the new record is later returned. This has to be fixed at some * point. */ if (F_ISSET(c, CURS_ACQUIRE)) { if ((rval = __bt_first(t, &c->key, ep, &exact)) == RET_ERROR) return (RET_ERROR); if (!exact) return (rval); /* * XXX * Kluge -- get, release, get the page. */ c->pg.pgno = ep->page->pgno; c->pg.index = ep->index; mpool_put(t->bt_mp, ep->page, 0); } /* Get the page referenced by the cursor. */ if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL) return (RET_ERROR); /* * Find the next/previous record in the tree and point the cursor at * it. The cursor may not be moved until a new key has been found. */ switch (flags) { case R_NEXT: /* Next record. */ /* * The cursor was deleted in duplicate records, and moved * forward to a record that has yet to be returned. Clear * that flag, and return the record. */ if (F_ISSET(c, CURS_AFTER)) goto usecurrent; idx = c->pg.index; if (++idx == NEXTINDEX(h)) { pg = h->nextpg; mpool_put(t->bt_mp, h, 0); if (pg == P_INVALID) return (RET_SPECIAL); if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); idx = 0; } break; case R_PREV: /* Previous record. */ /* * The cursor was deleted in duplicate records, and moved * backward to a record that has yet to be returned. Clear * that flag, and return the record. */ if (F_ISSET(c, CURS_BEFORE)) { usecurrent: F_CLR(c, CURS_AFTER | CURS_BEFORE); ep->page = h; ep->index = c->pg.index; return (RET_SUCCESS); } idx = c->pg.index; if (idx == 0) { pg = h->prevpg; mpool_put(t->bt_mp, h, 0); if (pg == P_INVALID) return (RET_SPECIAL); if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); idx = NEXTINDEX(h) - 1; } else --idx; break; } ep->page = h; ep->index = idx; return (RET_SUCCESS); } /* * __bt_first -- * Find the first entry. * * Parameters: * t: the tree * key: the key * erval: return EPG * exactp: pointer to exact match flag * * Returns: * The first entry in the tree greater than or equal to key, * or RET_SPECIAL if no such key exists. */ static int __bt_first(t, key, erval, exactp) BTREE *t; const DBT *key; EPG *erval; int *exactp; { PAGE *h; EPG *ep, save; db_pgno_t pg; /* * Find any matching record; __bt_search pins the page. * * If it's an exact match and duplicates are possible, walk backwards * in the tree until we find the first one. Otherwise, make sure it's * a valid key (__bt_search may return an index just past the end of a * page) and return it. */ if ((ep = __bt_search(t, key, exactp)) == NULL) return (RET_SPECIAL); if (*exactp) { if (F_ISSET(t, B_NODUPS)) { *erval = *ep; return (RET_SUCCESS); } /* * Walk backwards, as long as the entry matches and there are * keys left in the tree. Save a copy of each match in case * we go too far. */ save = *ep; h = ep->page; do { if (save.page->pgno != ep->page->pgno) { mpool_put(t->bt_mp, save.page, 0); save = *ep; } else save.index = ep->index; /* * Don't unpin the page the last (or original) match * was on, but make sure it's unpinned if an error * occurs. */ if (ep->index == 0) { if (h->prevpg == P_INVALID) break; if (h->pgno != save.page->pgno) mpool_put(t->bt_mp, h, 0); if ((h = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL) { if (h->pgno == save.page->pgno) mpool_put(t->bt_mp, save.page, 0); return (RET_ERROR); } ep->page = h; ep->index = NEXTINDEX(h); } --ep->index; } while (__bt_cmp(t, key, ep) == 0); /* * Reach here with the last page that was looked at pinned, * which may or may not be the same as the last (or original) * match page. If it's not useful, release it. */ if (h->pgno != save.page->pgno) mpool_put(t->bt_mp, h, 0); *erval = save; return (RET_SUCCESS); } /* If at the end of a page, find the next entry. */ if (ep->index == NEXTINDEX(ep->page)) { h = ep->page; pg = h->nextpg; mpool_put(t->bt_mp, h, 0); if (pg == P_INVALID) return (RET_SPECIAL); if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); ep->index = 0; ep->page = h; } *erval = *ep; return (RET_SUCCESS); } /* * __bt_setcur -- * Set the cursor to an entry in the tree. * * Parameters: * t: the tree * pgno: page number * index: page index */ void __bt_setcur(t, pgno, idx) BTREE *t; db_pgno_t pgno; u_int idx; { /* Lose any already deleted key. */ if (t->bt_cursor.key.data != NULL) { free(t->bt_cursor.key.data); t->bt_cursor.key.size = 0; t->bt_cursor.key.data = NULL; } F_CLR(&t->bt_cursor, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE); /* Update the cursor. */ t->bt_cursor.pg.pgno = pgno; t->bt_cursor.pg.index = idx; F_SET(&t->bt_cursor, CURS_INIT); } /* Recursive descent cursor. */ typedef struct rcursor_ { CURSOR cursor; size_t ssize; EPGNO *stack; EPGNO *sp; } RCURSOR; #define RCURSOR_MINSS 64 static int bt_rcinit(void **); static void bt_rcdestroy(void **); static int bt_rcpush(RCURSOR *, db_pgno_t, u_int); static EPGNO *bt_rcpop(RCURSOR *); static void bt_rcclr(RCURSOR *); static int bt_rcgrowstk(RCURSOR *); static int bt_rseqset(BTREE *, EPG *, DBT *, RCURSOR *, int); static int bt_rseqadv(BTREE *, EPG *, RCURSOR *, int); static int bt_rcinit(curs) void **curs; { RCURSOR *rc; rc = *curs = malloc(sizeof(RCURSOR)); if (rc == NULL) { errno = ENOMEM; return RET_ERROR; } memset(rc, 0, sizeof(*rc)); rc->ssize = RCURSOR_MINSS; rc->stack = malloc(rc->ssize * sizeof(EPGNO)); if (rc->stack == NULL) { free(rc); errno = ENOMEM; return RET_ERROR; } bt_rcclr(rc); return RET_SUCCESS; } static void bt_rcdestroy(curs) void **curs; { RCURSOR *rc; rc = *curs; free(rc->stack); free(rc); *curs = NULL; } static int bt_rcpush(rc, p, i) RCURSOR *rc; db_pgno_t p; u_int i; { int status; rc->sp->pgno = p; rc->sp->index = i; if (++rc->sp > rc->stack + rc->ssize) { status = bt_rcgrowstk(rc); if (status != RET_SUCCESS) return status; } return RET_SUCCESS; } static EPGNO * bt_rcpop(rc) RCURSOR *rc; { return (rc->sp == rc->stack) ? NULL : --rc->sp; } static void bt_rcclr(rc) RCURSOR *rc; { rc->sp = rc->stack; } static int bt_rcgrowstk(rc) RCURSOR *rc; { size_t osize; EPGNO *e; osize = rc->ssize; rc->ssize *= 2; e = realloc(rc->stack, rc->ssize * sizeof(EPGNO)); if (e == NULL) { rc->ssize = osize; errno = ENOMEM; return RET_ERROR; } rc->stack = e; return RET_SUCCESS; } /* * bt_rseq -- * Like __bt_seq but does recursive descent tree traversal * instead of using the prev/next pointers. */ int bt_rseq(dbp, key, data, curs, flags) const DB *dbp; DBT *key, *data; void **curs; u_int flags; { RCURSOR *rc; BTREE *t; EPG e; int status; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } if (curs == NULL) { errno = EINVAL; return RET_ERROR; } if (*curs == NULL) { status = bt_rcinit(curs); if (status != RET_SUCCESS) return RET_ERROR; } rc = *curs; /* * If scan unitialized as yet, or starting at a specific record, set * the scan to a specific key. Both bt_rseqset and bt_rseqadv pin * the page the cursor references if they're successful. */ switch (flags) { case R_NEXT: case R_PREV: if (F_ISSET(&rc->cursor, CURS_INIT)) { status = bt_rseqadv(t, &e, rc, flags); break; } /* FALLTHROUGH */ case R_FIRST: case R_LAST: case R_CURSOR: status = bt_rseqset(t, &e, key, rc, flags); break; default: errno = EINVAL; return (RET_ERROR); } if (status == RET_SUCCESS) { status = __bt_ret(t, &e, key, &t->bt_rkey, data, &t->bt_rdata, 0); /* * If the user is doing concurrent access, we copied the * key/data, toss the page. */ if (F_ISSET(t, B_DB_LOCK)) mpool_put(t->bt_mp, e.page, 0); else t->bt_pinned = e.page; } else if (status == RET_SPECIAL) bt_rcdestroy(curs); return (status); } /* * bt_rseqset -- * Set the sequential scan to a specific key. * * Parameters: * t: tree * ep: storage for returned key * key: key for initial scan position * rc: recursion cursor * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV * * Side effects: * Pins the page the cursor references. * Updates rc's stack and cursor. * * Returns: * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. */ static int bt_rseqset(t, ep, key, rc, flags) BTREE *t; EPG *ep; DBT *key; RCURSOR *rc; int flags; { PAGE *h; db_pgno_t pg; int status; /* * Find the first, last or specific key in the tree and point the * cursor at it. The cursor may not be moved until a new key has * been found. */ switch (flags) { case R_CURSOR: /* Not implemented. */ errno = EINVAL; return RET_ERROR; case R_FIRST: /* First record. */ case R_NEXT: bt_rcclr(rc); /* Walk down the left-hand side of the tree. */ for (pg = P_ROOT;;) { if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); /* Check for an empty tree. */ if (NEXTINDEX(h) == 0) { mpool_put(t->bt_mp, h, 0); return (RET_SPECIAL); } if (h->flags & (P_BLEAF | P_RLEAF)) break; pg = GETBINTERNAL(h, 0)->pgno; status = bt_rcpush(rc, h->pgno, 0); mpool_put(t->bt_mp, h, 0); if (status != RET_SUCCESS) return status; } ep->page = h; ep->index = 0; break; case R_LAST: /* Last record. */ case R_PREV: bt_rcclr(rc); /* Walk down the right-hand side of the tree. */ for (pg = P_ROOT;;) { if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); /* Check for an empty tree. */ if (NEXTINDEX(h) == 0) { mpool_put(t->bt_mp, h, 0); return (RET_SPECIAL); } if (h->flags & (P_BLEAF | P_RLEAF)) break; pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno; status = bt_rcpush(rc, h->pgno, NEXTINDEX(h) - 1); mpool_put(t->bt_mp, h, 0); if (status != RET_SUCCESS) return status; } ep->page = h; ep->index = NEXTINDEX(h) - 1; break; } rc->cursor.pg.pgno = ep->page->pgno; rc->cursor.pg.index = ep->index; F_CLR(&rc->cursor, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE); F_SET(&rc->cursor, CURS_INIT); return (RET_SUCCESS); } /* * bt_rseqadvance -- * Advance the sequential scan. * * Parameters: * t: tree * ep: return page * rc: recursion cursor * flags: R_NEXT, R_PREV * * Side effects: * Pins the page the new key/data record is on. * Updates rc's stack and cursor. * * Returns: * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. */ static int bt_rseqadv(t, ep, rc, flags) BTREE *t; EPG *ep; RCURSOR *rc; int flags; { CURSOR *c; PAGE *h; indx_t idx; db_pgno_t pg; int status; EPGNO *e; /* * There are a couple of states that we can be in. The cursor has * been initialized by the time we get here, but that's all we know. */ c = &rc->cursor; /* Get the page referenced by the cursor. */ if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL) return (RET_ERROR); /* * Find the next/previous record in the tree and point the cursor at * it. The cursor may not be moved until a new key has been found. */ switch (flags) { case R_NEXT: /* Next record. */ idx = c->pg.index; while (++idx == NEXTINDEX(h)) { /* Crawl up if we hit the right edge. */ e = bt_rcpop(rc); mpool_put(t->bt_mp, h, 0); if (e == NULL) /* Hit the right edge of root. */ return RET_SPECIAL; idx = e->index; pg = e->pgno; if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); } while (!(h->flags & (P_BLEAF | P_RLEAF))) { /* Crawl down the left until we hit a leaf. */ status = bt_rcpush(rc, h->pgno, idx); pg = GETBINTERNAL(h, idx)->pgno; mpool_put(t->bt_mp, h, 0); if (status != RET_SUCCESS) return status; if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); idx = 0; } break; case R_PREV: /* Previous record. */ idx = c->pg.index; while (!idx) { /* Crawl up if we hit the left edge. */ e = bt_rcpop(rc); mpool_put(t->bt_mp, h, 0); if (e == NULL) /* Hit the left edge of root. */ return RET_SPECIAL; idx = e->index; pg = e->pgno; if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); } idx--; while (!(h->flags & (P_BLEAF | P_RLEAF))) { /* Crawl down the right until we hit a leaf. */ status = bt_rcpush(rc, h->pgno, idx); pg = GETBINTERNAL(h, idx)->pgno; mpool_put(t->bt_mp, h, 0); if (status != RET_SUCCESS) return status; if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); idx = NEXTINDEX(h) - 1; } break; } ep->page = h; ep->index = idx; c->pg.pgno = h->pgno; c->pg.index = idx; F_CLR(c, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE); F_SET(c, CURS_INIT); return (RET_SUCCESS); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_split.c 8.10 (Berkeley) 1/9/95"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include "db-int.h" #include "btree.h" static int bt_broot __P((BTREE *, PAGE *, PAGE *, PAGE *)); static PAGE *bt_page __P((BTREE *, PAGE *, PAGE **, PAGE **, indx_t *, size_t)); static int bt_preserve __P((BTREE *, db_pgno_t)); static PAGE *bt_psplit __P((BTREE *, PAGE *, PAGE *, PAGE *, indx_t *, size_t)); static PAGE *bt_root __P((BTREE *, PAGE *, PAGE **, PAGE **, indx_t *, size_t)); static int bt_rroot __P((BTREE *, PAGE *, PAGE *, PAGE *)); static recno_t rec_total __P((PAGE *)); #ifdef STATISTICS u_long bt_rootsplit, bt_split, bt_sortsplit, bt_pfxsaved; #endif /* * __BT_SPLIT -- Split the tree. * * Parameters: * t: tree * sp: page to split * key: key to insert * data: data to insert * flags: BIGKEY/BIGDATA flags * ilen: insert length * skip: index to leave open * * Returns: * RET_ERROR, RET_SUCCESS */ int __bt_split(t, sp, key, data, flags, ilen, argskip) BTREE *t; PAGE *sp; const DBT *key, *data; int flags; size_t ilen; u_int32_t argskip; { BINTERNAL *bi; BLEAF *bl, *tbl; DBT a, b; EPGNO *parent; PAGE *h, *l, *r, *lchild, *rchild; indx_t nxtindex; u_int16_t skip; u_int32_t n, nbytes, nksize; int parentsplit; char *dest; /* * Split the page into two pages, l and r. The split routines return * a pointer to the page into which the key should be inserted and with * skip set to the offset which should be used. Additionally, l and r * are pinned. */ skip = argskip; h = sp->pgno == P_ROOT ? bt_root(t, sp, &l, &r, &skip, ilen) : bt_page(t, sp, &l, &r, &skip, ilen); if (h == NULL) return (RET_ERROR); /* * Insert the new key/data pair into the leaf page. (Key inserts * always cause a leaf page to split first.) */ h->linp[skip] = h->upper -= ilen; dest = (char *)h + h->upper; if (F_ISSET(t, R_RECNO)) WR_RLEAF(dest, data, flags) else WR_BLEAF(dest, key, data, flags) /* If the root page was split, make it look right. */ if (sp->pgno == P_ROOT && (F_ISSET(t, R_RECNO) ? bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR) goto err2; /* * Now we walk the parent page stack -- a LIFO stack of the pages that * were traversed when we searched for the page that split. Each stack * entry is a page number and a page index offset. The offset is for * the page traversed on the search. We've just split a page, so we * have to insert a new key into the parent page. * * If the insert into the parent page causes it to split, may have to * continue splitting all the way up the tree. We stop if the root * splits or the page inserted into didn't have to split to hold the * new key. Some algorithms replace the key for the old page as well * as the new page. We don't, as there's no reason to believe that the * first key on the old page is any better than the key we have, and, * in the case of a key being placed at index 0 causing the split, the * key is unavailable. * * There are a maximum of 5 pages pinned at any time. We keep the left * and right pages pinned while working on the parent. The 5 are the * two children, left parent and right parent (when the parent splits) * and the root page or the overflow key page when calling bt_preserve. * This code must make sure that all pins are released other than the * root page or overflow page which is unlocked elsewhere. */ while ((parent = BT_POP(t)) != NULL) { lchild = l; rchild = r; /* Get the parent page. */ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) goto err2; /* * The new key goes ONE AFTER the index, because the split * was to the right. */ skip = parent->index + 1; /* * Calculate the space needed on the parent page. * * Prefix trees: space hack when inserting into BINTERNAL * pages. Retain only what's needed to distinguish between * the new entry and the LAST entry on the page to its left. * If the keys compare equal, retain the entire key. Note, * we don't touch overflow keys, and the entire key must be * retained for the next-to-left most key on the leftmost * page of each level, or the search will fail. Applicable * ONLY to internal pages that have leaf pages as children. * Further reduction of the key between pairs of internal * pages loses too much information. */ switch (rchild->flags & P_TYPE) { case P_BINTERNAL: bi = GETBINTERNAL(rchild, 0); nbytes = NBINTERNAL(bi->ksize); break; case P_BLEAF: bl = GETBLEAF(rchild, 0); nbytes = NBINTERNAL(bl->ksize); if (t->bt_pfx && !(bl->flags & P_BIGKEY) && (h->prevpg != P_INVALID || skip > 1)) { tbl = GETBLEAF(lchild, NEXTINDEX(lchild) - 1); a.size = tbl->ksize; a.data = tbl->bytes; b.size = bl->ksize; b.data = bl->bytes; nksize = t->bt_pfx(&a, &b); n = NBINTERNAL(nksize); if (n < nbytes) { #ifdef STATISTICS bt_pfxsaved += nbytes - n; #endif nbytes = n; } else nksize = 0; } else nksize = 0; break; case P_RINTERNAL: case P_RLEAF: nbytes = NRINTERNAL; break; default: abort(); } /* Split the parent page if necessary or shift the indices. */ if (h->upper - h->lower < nbytes + sizeof(indx_t)) { sp = h; h = h->pgno == P_ROOT ? bt_root(t, h, &l, &r, &skip, nbytes) : bt_page(t, h, &l, &r, &skip, nbytes); if (h == NULL) goto err1; parentsplit = 1; } else { if (skip < (nxtindex = NEXTINDEX(h))) memmove(h->linp + skip + 1, h->linp + skip, (nxtindex - skip) * sizeof(indx_t)); h->lower += sizeof(indx_t); parentsplit = 0; } /* Insert the key into the parent page. */ switch (rchild->flags & P_TYPE) { case P_BINTERNAL: h->linp[skip] = h->upper -= nbytes; dest = (char *)h + h->linp[skip]; memmove(dest, bi, nbytes); ((BINTERNAL *)dest)->pgno = rchild->pgno; break; case P_BLEAF: h->linp[skip] = h->upper -= nbytes; dest = (char *)h + h->linp[skip]; WR_BINTERNAL(dest, nksize ? nksize : bl->ksize, rchild->pgno, bl->flags & P_BIGKEY); memmove(dest, bl->bytes, nksize ? nksize : bl->ksize); if (bl->flags & P_BIGKEY && bt_preserve(t, *(db_pgno_t *)bl->bytes) == RET_ERROR) goto err1; break; case P_RINTERNAL: /* * Update the left page count. If split * added at index 0, fix the correct page. */ if (skip > 0) dest = (char *)h + h->linp[skip - 1]; else dest = (char *)l + l->linp[NEXTINDEX(l) - 1]; ((RINTERNAL *)dest)->nrecs = rec_total(lchild); ((RINTERNAL *)dest)->pgno = lchild->pgno; /* Update the right page count. */ h->linp[skip] = h->upper -= nbytes; dest = (char *)h + h->linp[skip]; ((RINTERNAL *)dest)->nrecs = rec_total(rchild); ((RINTERNAL *)dest)->pgno = rchild->pgno; break; case P_RLEAF: /* * Update the left page count. If split * added at index 0, fix the correct page. */ if (skip > 0) dest = (char *)h + h->linp[skip - 1]; else dest = (char *)l + l->linp[NEXTINDEX(l) - 1]; ((RINTERNAL *)dest)->nrecs = NEXTINDEX(lchild); ((RINTERNAL *)dest)->pgno = lchild->pgno; /* Update the right page count. */ h->linp[skip] = h->upper -= nbytes; dest = (char *)h + h->linp[skip]; ((RINTERNAL *)dest)->nrecs = NEXTINDEX(rchild); ((RINTERNAL *)dest)->pgno = rchild->pgno; break; default: abort(); } /* Unpin the held pages. */ if (!parentsplit) { mpool_put(t->bt_mp, h, MPOOL_DIRTY); break; } /* If the root page was split, make it look right. */ if (sp->pgno == P_ROOT && (F_ISSET(t, R_RECNO) ? bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR) goto err1; mpool_put(t->bt_mp, lchild, MPOOL_DIRTY); mpool_put(t->bt_mp, rchild, MPOOL_DIRTY); } /* Unpin the held pages. */ mpool_put(t->bt_mp, l, MPOOL_DIRTY); mpool_put(t->bt_mp, r, MPOOL_DIRTY); /* Clear any pages left on the stack. */ return (RET_SUCCESS); /* * If something fails in the above loop we were already walking back * up the tree and the tree is now inconsistent. Nothing much we can * do about it but release any memory we're holding. */ err1: mpool_put(t->bt_mp, lchild, MPOOL_DIRTY); mpool_put(t->bt_mp, rchild, MPOOL_DIRTY); err2: mpool_put(t->bt_mp, l, 0); mpool_put(t->bt_mp, r, 0); __dbpanic(t->bt_dbp); return (RET_ERROR); } /* * BT_PAGE -- Split a non-root page of a btree. * * Parameters: * t: tree * h: root page * lp: pointer to left page pointer * rp: pointer to right page pointer * skip: pointer to index to leave open * ilen: insert length * * Returns: * Pointer to page in which to insert or NULL on error. */ static PAGE * bt_page(t, h, lp, rp, skip, ilen) BTREE *t; PAGE *h, **lp, **rp; indx_t *skip; size_t ilen; { PAGE *l, *r, *tp; db_pgno_t npg; #ifdef STATISTICS ++bt_split; #endif /* Put the new right page for the split into place. */ if ((r = __bt_new(t, &npg)) == NULL) return (NULL); r->pgno = npg; r->lower = BTDATAOFF; r->upper = t->bt_psize; r->nextpg = h->nextpg; r->prevpg = h->pgno; r->flags = h->flags & P_TYPE; /* * If we're splitting the last page on a level because we're appending * a key to it (skip is NEXTINDEX()), it's likely that the data is * sorted. Adding an empty page on the side of the level is less work * and can push the fill factor much higher than normal. If we're * wrong it's no big deal, we'll just do the split the right way next * time. It may look like it's equally easy to do a similar hack for * reverse sorted data, that is, split the tree left, but it's not. * Don't even try. */ if (h->nextpg == P_INVALID && *skip == NEXTINDEX(h)) { #ifdef STATISTICS ++bt_sortsplit; #endif h->nextpg = r->pgno; r->lower = BTDATAOFF + sizeof(indx_t); *skip = 0; *lp = h; *rp = r; return (r); } /* Put the new left page for the split into place. */ if ((l = (PAGE *)malloc(t->bt_psize)) == NULL) { mpool_put(t->bt_mp, r, 0); return (NULL); } #ifdef PURIFY memset(l, 0xff, t->bt_psize); #endif l->pgno = h->pgno; l->nextpg = r->pgno; l->prevpg = h->prevpg; l->lower = BTDATAOFF; l->upper = t->bt_psize; l->flags = h->flags & P_TYPE; /* Fix up the previous pointer of the page after the split page. */ if (h->nextpg != P_INVALID) { if ((tp = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL) { free(l); /* XXX mpool_free(t->bt_mp, r->pgno); */ return (NULL); } tp->prevpg = r->pgno; mpool_put(t->bt_mp, tp, MPOOL_DIRTY); } /* * Split right. The key/data pairs aren't sorted in the btree page so * it's simpler to copy the data from the split page onto two new pages * instead of copying half the data to the right page and compacting * the left page in place. Since the left page can't change, we have * to swap the original and the allocated left page after the split. */ tp = bt_psplit(t, h, l, r, skip, ilen); /* Move the new left page onto the old left page. */ memmove(h, l, t->bt_psize); if (tp == l) tp = h; free(l); *lp = h; *rp = r; return (tp); } /* * BT_ROOT -- Split the root page of a btree. * * Parameters: * t: tree * h: root page * lp: pointer to left page pointer * rp: pointer to right page pointer * skip: pointer to index to leave open * ilen: insert length * * Returns: * Pointer to page in which to insert or NULL on error. */ static PAGE * bt_root(t, h, lp, rp, skip, ilen) BTREE *t; PAGE *h, **lp, **rp; indx_t *skip; size_t ilen; { PAGE *l, *r, *tp; db_pgno_t lnpg, rnpg; #ifdef STATISTICS ++bt_split; ++bt_rootsplit; #endif /* Put the new left and right pages for the split into place. */ if ((l = __bt_new(t, &lnpg)) == NULL || (r = __bt_new(t, &rnpg)) == NULL) return (NULL); l->pgno = lnpg; r->pgno = rnpg; l->nextpg = r->pgno; r->prevpg = l->pgno; l->prevpg = r->nextpg = P_INVALID; l->lower = r->lower = BTDATAOFF; l->upper = r->upper = t->bt_psize; l->flags = r->flags = h->flags & P_TYPE; /* Split the root page. */ tp = bt_psplit(t, h, l, r, skip, ilen); *lp = l; *rp = r; return (tp); } /* * BT_RROOT -- Fix up the recno root page after it has been split. * * Parameters: * t: tree * h: root page * l: left page * r: right page * * Returns: * RET_ERROR, RET_SUCCESS */ static int bt_rroot(t, h, l, r) BTREE *t; PAGE *h, *l, *r; { char *dest; /* Insert the left and right keys, set the header information. */ h->linp[0] = h->upper = t->bt_psize - NRINTERNAL; dest = (char *)h + h->upper; WR_RINTERNAL(dest, l->flags & P_RLEAF ? NEXTINDEX(l) : rec_total(l), l->pgno); h->linp[1] = h->upper -= NRINTERNAL; dest = (char *)h + h->upper; WR_RINTERNAL(dest, r->flags & P_RLEAF ? NEXTINDEX(r) : rec_total(r), r->pgno); h->lower = BTDATAOFF + 2 * sizeof(indx_t); /* Unpin the root page, set to recno internal page. */ h->flags &= ~P_TYPE; h->flags |= P_RINTERNAL; mpool_put(t->bt_mp, h, MPOOL_DIRTY); return (RET_SUCCESS); } /* * BT_BROOT -- Fix up the btree root page after it has been split. * * Parameters: * t: tree * h: root page * l: left page * r: right page * * Returns: * RET_ERROR, RET_SUCCESS */ static int bt_broot(t, h, l, r) BTREE *t; PAGE *h, *l, *r; { BINTERNAL *bi; BLEAF *bl; u_int32_t nbytes; char *dest; /* * If the root page was a leaf page, change it into an internal page. * We copy the key we split on (but not the key's data, in the case of * a leaf page) to the new root page. * * The btree comparison code guarantees that the left-most key on any * level of the tree is never used, so it doesn't need to be filled in. */ nbytes = NBINTERNAL(0); h->linp[0] = h->upper = t->bt_psize - nbytes; dest = (char *)h + h->upper; WR_BINTERNAL(dest, 0, l->pgno, 0); switch (h->flags & P_TYPE) { case P_BLEAF: bl = GETBLEAF(r, 0); nbytes = NBINTERNAL(bl->ksize); h->linp[1] = h->upper -= nbytes; dest = (char *)h + h->upper; WR_BINTERNAL(dest, bl->ksize, r->pgno, 0); memmove(dest, bl->bytes, bl->ksize); /* * If the key is on an overflow page, mark the overflow chain * so it isn't deleted when the leaf copy of the key is deleted. */ if (bl->flags & P_BIGKEY && bt_preserve(t, *(db_pgno_t *)bl->bytes) == RET_ERROR) return (RET_ERROR); break; case P_BINTERNAL: bi = GETBINTERNAL(r, 0); nbytes = NBINTERNAL(bi->ksize); h->linp[1] = h->upper -= nbytes; dest = (char *)h + h->upper; memmove(dest, bi, nbytes); ((BINTERNAL *)dest)->pgno = r->pgno; break; default: abort(); } /* There are two keys on the page. */ h->lower = BTDATAOFF + 2 * sizeof(indx_t); /* Unpin the root page, set to btree internal page. */ h->flags &= ~P_TYPE; h->flags |= P_BINTERNAL; mpool_put(t->bt_mp, h, MPOOL_DIRTY); return (RET_SUCCESS); } /* * BT_PSPLIT -- Do the real work of splitting the page. * * Parameters: * t: tree * h: page to be split * l: page to put lower half of data * r: page to put upper half of data * pskip: pointer to index to leave open * ilen: insert length * * Returns: * Pointer to page in which to insert. */ static PAGE * bt_psplit(t, h, l, r, pskip, ilen) BTREE *t; PAGE *h, *l, *r; indx_t *pskip; size_t ilen; { BINTERNAL *bi; BLEAF *bl; CURSOR *c; RLEAF *rl; PAGE *rval; void *src; indx_t full, half, nxt, off, skip, top, used; u_int32_t nbytes; int bigkeycnt, isbigkey; /* * Split the data to the left and right pages. Leave the skip index * open. Additionally, make some effort not to split on an overflow * key. This makes internal page processing faster and can save * space as overflow keys used by internal pages are never deleted. */ bigkeycnt = 0; skip = *pskip; full = t->bt_psize - BTDATAOFF; half = full / 2; used = 0; for (nxt = off = 0, top = NEXTINDEX(h); nxt < top; ++off) { if (skip == off) { nbytes = ilen; isbigkey = 0; /* XXX: not really known. */ } else switch (h->flags & P_TYPE) { case P_BINTERNAL: src = bi = GETBINTERNAL(h, nxt); nbytes = NBINTERNAL(bi->ksize); isbigkey = bi->flags & P_BIGKEY; break; case P_BLEAF: src = bl = GETBLEAF(h, nxt); nbytes = NBLEAF(bl); isbigkey = bl->flags & P_BIGKEY; break; case P_RINTERNAL: src = GETRINTERNAL(h, nxt); nbytes = NRINTERNAL; isbigkey = 0; break; case P_RLEAF: src = rl = GETRLEAF(h, nxt); nbytes = NRLEAF(rl); isbigkey = 0; break; default: abort(); } /* * If the key/data pairs are substantial fractions of the max * possible size for the page, it's possible to get situations * where we decide to try and copy too much onto the left page. * Make sure that doesn't happen. */ if ((skip <= off && used + nbytes + sizeof(indx_t) >= full) || nxt == top - 1) { --off; break; } /* Copy the key/data pair, if not the skipped index. */ if (skip != off) { ++nxt; l->linp[off] = l->upper -= nbytes; memmove((char *)l + l->upper, src, nbytes); } used += nbytes + sizeof(indx_t); if (used >= half) { if (!isbigkey || bigkeycnt == 3) break; else ++bigkeycnt; } } /* * Off is the last offset that's valid for the left page. * Nxt is the first offset to be placed on the right page. */ l->lower += (off + 1) * sizeof(indx_t); /* * If splitting the page that the cursor was on, the cursor has to be * adjusted to point to the same record as before the split. If the * cursor is at or past the skipped slot, the cursor is incremented by * one. If the cursor is on the right page, it is decremented by the * number of records split to the left page. */ c = &t->bt_cursor; if (F_ISSET(c, CURS_INIT) && c->pg.pgno == h->pgno) { if (c->pg.index >= skip) ++c->pg.index; if (c->pg.index < nxt) /* Left page. */ c->pg.pgno = l->pgno; else { /* Right page. */ c->pg.pgno = r->pgno; c->pg.index -= nxt; } } /* * If the skipped index was on the left page, just return that page. * Otherwise, adjust the skip index to reflect the new position on * the right page. */ if (skip <= off) { /* * If we get here then 'skip' is in the left page. We do * not want to mix this with the right page, so we assign * an unrealistic value (-1). */ skip = (indx_t)-1; rval = l; } else { rval = r; *pskip -= nxt; } for (off = 0; nxt < top; ++off) { if (skip == nxt) { ++off; /* * Assign 'skip' an unrealistic value (-1) to ensure * it is not matched again. */ skip = (indx_t)-1; } switch (h->flags & P_TYPE) { case P_BINTERNAL: src = bi = GETBINTERNAL(h, nxt); nbytes = NBINTERNAL(bi->ksize); break; case P_BLEAF: src = bl = GETBLEAF(h, nxt); nbytes = NBLEAF(bl); break; case P_RINTERNAL: src = GETRINTERNAL(h, nxt); nbytes = NRINTERNAL; break; case P_RLEAF: src = rl = GETRLEAF(h, nxt); nbytes = NRLEAF(rl); break; default: abort(); } ++nxt; r->linp[off] = r->upper -= nbytes; memmove((char *)r + r->upper, src, nbytes); } r->lower += off * sizeof(indx_t); /* If the key is being appended to the page, adjust the index. */ if (skip == top) r->lower += sizeof(indx_t); return (rval); } /* * BT_PRESERVE -- Mark a chain of pages as used by an internal node. * * Chains of indirect blocks pointed to by leaf nodes get reclaimed when the * record that references them gets deleted. Chains pointed to by internal * pages never get deleted. This routine marks a chain as pointed to by an * internal page. * * Parameters: * t: tree * pg: page number of first page in the chain. * * Returns: * RET_SUCCESS, RET_ERROR. */ static int bt_preserve(t, pg) BTREE *t; db_pgno_t pg; { PAGE *h; if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) return (RET_ERROR); h->flags |= P_PRESERVE; mpool_put(t->bt_mp, h, MPOOL_DIRTY); return (RET_SUCCESS); } /* * REC_TOTAL -- Return the number of recno entries below a page. * * Parameters: * h: page * * Returns: * The number of recno entries below a page. * * XXX * These values could be set by the bt_psplit routine. The problem is that the * entry has to be popped off of the stack etc. or the values have to be passed * all the way back to bt_split/bt_rroot and it's not very clean. */ static recno_t rec_total(h) PAGE *h; { recno_t recs; indx_t nxt, top; for (recs = 0, nxt = 0, top = NEXTINDEX(h); nxt < top; ++nxt) recs += GETRINTERNAL(h, nxt)->nrecs; return (recs); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_utils.c 8.8 (Berkeley) 7/20/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include "db-int.h" #include "btree.h" /* * __bt_ret -- * Build return key/data pair. * * Parameters: * t: tree * e: key/data pair to be returned * key: user's key structure (NULL if not to be filled in) * rkey: memory area to hold key * data: user's data structure (NULL if not to be filled in) * rdata: memory area to hold data * copy: always copy the key/data item * * Returns: * RET_SUCCESS, RET_ERROR. */ int __bt_ret(t, e, key, rkey, data, rdata, copy) BTREE *t; EPG *e; DBT *key, *rkey, *data, *rdata; int copy; { BLEAF *bl; void *p; bl = GETBLEAF(e->page, e->index); /* * We must copy big keys/data to make them contigous. Otherwise, * leave the page pinned and don't copy unless the user specified * concurrent access. */ if (key == NULL) goto dataonly; if (bl->flags & P_BIGKEY) { if (__ovfl_get(t, bl->bytes, &key->size, &rkey->data, &rkey->size)) return (RET_ERROR); key->data = rkey->data; } else if (copy || F_ISSET(t, B_DB_LOCK)) { if (bl->ksize > rkey->size) { p = (void *)(rkey->data == NULL ? malloc(bl->ksize) : realloc(rkey->data, bl->ksize)); if (p == NULL) return (RET_ERROR); rkey->data = p; rkey->size = bl->ksize; } memmove(rkey->data, bl->bytes, bl->ksize); key->size = bl->ksize; key->data = rkey->data; } else { key->size = bl->ksize; key->data = bl->bytes; } dataonly: if (data == NULL) return (RET_SUCCESS); if (bl->flags & P_BIGDATA) { if (__ovfl_get(t, bl->bytes + bl->ksize, &data->size, &rdata->data, &rdata->size)) return (RET_ERROR); data->data = rdata->data; } else if (copy || F_ISSET(t, B_DB_LOCK)) { /* Use +1 in case the first record retrieved is 0 length. */ if (bl->dsize + 1 > rdata->size) { p = (void *)(rdata->data == NULL ? malloc(bl->dsize + 1) : realloc(rdata->data, bl->dsize + 1)); if (p == NULL) return (RET_ERROR); rdata->data = p; rdata->size = bl->dsize + 1; } memmove(rdata->data, bl->bytes + bl->ksize, bl->dsize); data->size = bl->dsize; data->data = rdata->data; } else { data->size = bl->dsize; data->data = bl->bytes + bl->ksize; } return (RET_SUCCESS); } /* * __BT_CMP -- Compare a key to a given record. * * Parameters: * t: tree * k1: DBT pointer of first arg to comparison * e: pointer to EPG for comparison * * Returns: * < 0 if k1 is < record * = 0 if k1 is = record * > 0 if k1 is > record */ int __bt_cmp(t, k1, e) BTREE *t; const DBT *k1; EPG *e; { BINTERNAL *bi; BLEAF *bl; DBT k2; PAGE *h; void *bigkey; /* * The left-most key on internal pages, at any level of the tree, is * guaranteed by the following code to be less than any user key. * This saves us from having to update the leftmost key on an internal * page when the user inserts a new key in the tree smaller than * anything we've yet seen. */ h = e->page; if (e->index == 0 && h->prevpg == P_INVALID && !(h->flags & P_BLEAF)) return (1); bigkey = NULL; if (h->flags & P_BLEAF) { bl = GETBLEAF(h, e->index); if (bl->flags & P_BIGKEY) bigkey = bl->bytes; else { k2.data = bl->bytes; k2.size = bl->ksize; } } else { bi = GETBINTERNAL(h, e->index); if (bi->flags & P_BIGKEY) bigkey = bi->bytes; else { k2.data = bi->bytes; k2.size = bi->ksize; } } if (bigkey) { if (__ovfl_get(t, bigkey, &k2.size, &t->bt_rdata.data, &t->bt_rdata.size)) return (RET_ERROR); k2.data = t->bt_rdata.data; } return ((*t->bt_cmp)(k1, &k2)); } /* * __BT_DEFCMP -- Default comparison routine. * * Parameters: * a: DBT #1 * b: DBT #2 * * Returns: * < 0 if a is < b * = 0 if a is = b * > 0 if a is > b */ int __bt_defcmp(a, b) const DBT *a, *b; { register size_t len; register u_char *p1, *p2; /* * XXX * If a size_t doesn't fit in an int, this routine can lose. * What we need is a integral type which is guaranteed to be * larger than a size_t, and there is no such thing. */ len = MIN(a->size, b->size); for (p1 = a->data, p2 = b->data; len--; ++p1, ++p2) if (*p1 != *p2) return ((int)*p1 - (int)*p2); return ((int)a->size - (int)b->size); } /* * __BT_DEFPFX -- Default prefix routine. * * Parameters: * a: DBT #1 * b: DBT #2 * * Returns: * Number of bytes needed to distinguish b from a. */ size_t __bt_defpfx(a, b) const DBT *a, *b; { register u_char *p1, *p2; register size_t cnt, len; cnt = 1; len = MIN(a->size, b->size); for (p1 = a->data, p2 = b->data; len--; ++p1, ++p2, ++cnt) if (*p1 != *p2) return (cnt); /* a->size must be <= b->size, or they wouldn't be in this order. */ return (a->size < b->size ? a->size + 1 : a->size); } /* * Copyright (c) 1997-2000 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _KRB5_BTREE_H #define _KRB5_BTREE_H #ifdef __cplusplus extern "C" { #endif /*- * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)btree.h 8.11 (Berkeley) 8/17/94 */ /* Macros to set/clear/test flags. */ #define F_SET(p, f) (p)->flags |= (f) #define F_CLR(p, f) (p)->flags &= ~(f) #define F_ISSET(p, f) ((p)->flags & (f)) #include "mpool.h" #define DEFMINKEYPAGE (2) /* Minimum keys per page */ #define MINCACHE (5) /* Minimum cached pages */ #define MINPSIZE (512) /* Minimum page size */ /* * Page 0 of a btree file contains a copy of the meta-data. This page is also * used as an out-of-band page, i.e. page pointers that point to nowhere point * to page 0. Page 1 is the root of the btree. */ #define P_INVALID 0 /* Invalid tree page number. */ #define P_META 0 /* Tree metadata page number. */ #define P_ROOT 1 /* Tree root page number. */ /* * There are five page layouts in the btree: btree internal pages (BINTERNAL), * btree leaf pages (BLEAF), recno internal pages (RINTERNAL), recno leaf pages * (RLEAF) and overflow pages. All five page types have a page header (PAGE). * This implementation requires that values within structures NOT be padded. * (ANSI C permits random padding.) If your compiler pads randomly you'll have * to do some work to get this package to run. */ typedef struct _page { db_pgno_t pgno; /* this page's page number */ db_pgno_t prevpg; /* left sibling */ db_pgno_t nextpg; /* right sibling */ #define P_BINTERNAL 0x01 /* btree internal page */ #define P_BLEAF 0x02 /* leaf page */ #define P_OVERFLOW 0x04 /* overflow page */ #define P_RINTERNAL 0x08 /* recno internal page */ #define P_RLEAF 0x10 /* leaf page */ #define P_TYPE 0x1f /* type mask */ #define P_PRESERVE 0x20 /* never delete this chain of pages */ u_int32_t flags; indx_t lower; /* lower bound of free space on page */ indx_t upper; /* upper bound of free space on page */ indx_t linp[1]; /* indx_t-aligned VAR. LENGTH DATA */ } PAGE; /* First and next index. */ #define BTDATAOFF \ (sizeof(db_pgno_t) + sizeof(db_pgno_t) + sizeof(db_pgno_t) + \ sizeof(u_int32_t) + sizeof(indx_t) + sizeof(indx_t)) #define NEXTINDEX(p) (((p)->lower - BTDATAOFF) / sizeof(indx_t)) /* * For pages other than overflow pages, there is an array of offsets into the * rest of the page immediately following the page header. Each offset is to * an item which is unique to the type of page. The h_lower offset is just * past the last filled-in index. The h_upper offset is the first item on the * page. Offsets are from the beginning of the page. * * If an item is too big to store on a single page, a flag is set and the item * is a { page, size } pair such that the page is the first page of an overflow * chain with size bytes of item. Overflow pages are simply bytes without any * external structure. * * The page number and size fields in the items are db_pgno_t-aligned so they can * be manipulated without copying. (This presumes that 32 bit items can be * manipulated on this system.) */ #define LALIGN(n) (((n) + sizeof(db_pgno_t) - 1) & ~(sizeof(db_pgno_t) - 1)) #define NOVFLSIZE (sizeof(db_pgno_t) + sizeof(u_int32_t)) /* * For the btree internal pages, the item is a key. BINTERNALs are {key, pgno} * pairs, such that the key compares less than or equal to all of the records * on that page. For a tree without duplicate keys, an internal page with two * consecutive keys, a and b, will have all records greater than or equal to a * and less than b stored on the page associated with a. Duplicate keys are * somewhat special and can cause duplicate internal and leaf page records and * some minor modifications of the above rule. */ typedef struct _binternal { u_int32_t ksize; /* key size */ db_pgno_t pgno; /* page number stored on */ #define P_BIGDATA 0x01 /* overflow data */ #define P_BIGKEY 0x02 /* overflow key */ u_char flags; char bytes[1]; /* data */ } BINTERNAL; /* Get the page's BINTERNAL structure at index indx. */ #define GETBINTERNAL(pg, indx) \ ((BINTERNAL *)((char *)(pg) + (pg)->linp[indx])) /* Get the number of bytes in the entry. */ #define NBINTERNAL(len) \ LALIGN(sizeof(u_int32_t) + sizeof(db_pgno_t) + sizeof(u_char) + (len)) /* Copy a BINTERNAL entry to the page. */ #define WR_BINTERNAL(p, size, pgno, flags) { \ *(u_int32_t *)p = size; \ p += sizeof(u_int32_t); \ *(db_pgno_t *)p = pgno; \ p += sizeof(db_pgno_t); \ *(u_char *)p = flags; \ p += sizeof(u_char); \ } /* * For the recno internal pages, the item is a page number with the number of * keys found on that page and below. */ typedef struct _rinternal { recno_t nrecs; /* number of records */ db_pgno_t pgno; /* page number stored below */ } RINTERNAL; /* Get the page's RINTERNAL structure at index indx. */ #define GETRINTERNAL(pg, indx) \ ((RINTERNAL *)((char *)(pg) + (pg)->linp[indx])) /* Get the number of bytes in the entry. */ #define NRINTERNAL \ LALIGN(sizeof(recno_t) + sizeof(db_pgno_t)) /* Copy a RINTERAL entry to the page. */ #define WR_RINTERNAL(p, nrecs, pgno) { \ *(recno_t *)p = nrecs; \ p += sizeof(recno_t); \ *(db_pgno_t *)p = pgno; \ } /* For the btree leaf pages, the item is a key and data pair. */ typedef struct _bleaf { u_int32_t ksize; /* size of key */ u_int32_t dsize; /* size of data */ u_char flags; /* P_BIGDATA, P_BIGKEY */ char bytes[1]; /* data */ } BLEAF; /* Get the page's BLEAF structure at index indx. */ #define GETBLEAF(pg, indx) \ ((BLEAF *)((char *)(pg) + (pg)->linp[indx])) /* Get the number of bytes in the entry. */ #define NBLEAF(p) NBLEAFDBT((p)->ksize, (p)->dsize) /* Get the number of bytes in the user's key/data pair. */ #define NBLEAFDBT(ksize, dsize) \ LALIGN(sizeof(u_int32_t) + sizeof(u_int32_t) + sizeof(u_char) + \ (ksize) + (dsize)) /* Copy a BLEAF entry to the page. */ #define WR_BLEAF(p, key, data, flags) { \ *(u_int32_t *)p = key->size; \ p += sizeof(u_int32_t); \ *(u_int32_t *)p = data->size; \ p += sizeof(u_int32_t); \ *(u_char *)p = flags; \ p += sizeof(u_char); \ memmove(p, key->data, key->size); \ p += key->size; \ memmove(p, data->data, data->size); \ } /* For the recno leaf pages, the item is a data entry. */ typedef struct _rleaf { u_int32_t dsize; /* size of data */ u_char flags; /* P_BIGDATA */ char bytes[1]; } RLEAF; /* Get the page's RLEAF structure at index indx. */ #define GETRLEAF(pg, indx) \ ((RLEAF *)((char *)(pg) + (pg)->linp[indx])) /* Get the number of bytes in the entry. */ #define NRLEAF(p) NRLEAFDBT((p)->dsize) /* Get the number of bytes from the user's data. */ #define NRLEAFDBT(dsize) \ LALIGN(sizeof(u_int32_t) + sizeof(u_char) + (dsize)) /* Copy a RLEAF entry to the page. */ #define WR_RLEAF(p, data, flags) { \ *(u_int32_t *)p = data->size; \ p += sizeof(u_int32_t); \ *(u_char *)p = flags; \ p += sizeof(u_char); \ memmove(p, data->data, data->size); \ } /* * A record in the tree is either a pointer to a page and an index in the page * or a page number and an index. These structures are used as a cursor, stack * entry and search returns as well as to pass records to other routines. * * One comment about searches. Internal page searches must find the largest * record less than key in the tree so that descents work. Leaf page searches * must find the smallest record greater than key so that the returned index * is the record's correct position for insertion. */ typedef struct _epgno { db_pgno_t pgno; /* the page number */ indx_t index; /* the index on the page */ } EPGNO; typedef struct _epg { PAGE *page; /* the (pinned) page */ indx_t index; /* the index on the page */ } EPG; /* * About cursors. The cursor (and the page that contained the key/data pair * that it referenced) can be deleted, which makes things a bit tricky. If * there are no duplicates of the cursor key in the tree (i.e. B_NODUPS is set * or there simply aren't any duplicates of the key) we copy the key that it * referenced when it's deleted, and reacquire a new cursor key if the cursor * is used again. If there are duplicates keys, we move to the next/previous * key, and set a flag so that we know what happened. NOTE: if duplicate (to * the cursor) keys are added to the tree during this process, it is undefined * if they will be returned or not in a cursor scan. * * The flags determine the possible states of the cursor: * * CURS_INIT The cursor references *something*. * CURS_ACQUIRE The cursor was deleted, and a key has been saved so that * we can reacquire the right position in the tree. * CURS_AFTER, CURS_BEFORE * The cursor was deleted, and now references a key/data pair * that has not yet been returned, either before or after the * deleted key/data pair. * XXX * This structure is broken out so that we can eventually offer multiple * cursors as part of the DB interface. */ typedef struct _cursor { EPGNO pg; /* B: Saved tree reference. */ DBT key; /* B: Saved key, or key.data == NULL. */ recno_t rcursor; /* R: recno cursor (1-based) */ #define CURS_ACQUIRE 0x01 /* B: Cursor needs to be reacquired. */ #define CURS_AFTER 0x02 /* B: Unreturned cursor after key. */ #define CURS_BEFORE 0x04 /* B: Unreturned cursor before key. */ #define CURS_INIT 0x08 /* RB: Cursor initialized. */ u_int8_t flags; } CURSOR; /* * The metadata of the tree. The nrecs field is used only by the RECNO code. * This is because the btree doesn't really need it and it requires that every * put or delete call modify the metadata. */ typedef struct _btmeta { u_int32_t magic; /* magic number */ u_int32_t version; /* version */ u_int32_t psize; /* page size */ u_int32_t free; /* page number of first free page */ u_int32_t nrecs; /* R: number of records */ #define SAVEMETA (B_NODUPS | R_RECNO) u_int32_t flags; /* bt_flags & SAVEMETA */ } BTMETA; /* The in-memory btree/recno data structure. */ typedef struct _btree { MPOOL *bt_mp; /* memory pool cookie */ DB *bt_dbp; /* pointer to enclosing DB */ EPG bt_cur; /* current (pinned) page */ PAGE *bt_pinned; /* page pinned across calls */ CURSOR bt_cursor; /* cursor */ #define BT_PUSH(t, p, i) { \ t->bt_sp->pgno = p; \ t->bt_sp->index = i; \ ++t->bt_sp; \ } #define BT_POP(t) (t->bt_sp == t->bt_stack ? NULL : --t->bt_sp) #define BT_CLR(t) (t->bt_sp = t->bt_stack) EPGNO bt_stack[50]; /* stack of parent pages */ EPGNO *bt_sp; /* current stack pointer */ DBT bt_rkey; /* returned key */ DBT bt_rdata; /* returned data */ int bt_fd; /* tree file descriptor */ db_pgno_t bt_free; /* next free page */ u_int32_t bt_psize; /* page size */ indx_t bt_ovflsize; /* cut-off for key/data overflow */ int bt_lorder; /* byte order */ /* sorted order */ enum { NOT, BACK, FORWARD } bt_order; EPGNO bt_last; /* last insert */ /* B: key comparison function */ int (*bt_cmp) __P((const DBT *, const DBT *)); /* B: prefix comparison function */ size_t (*bt_pfx) __P((const DBT *, const DBT *)); /* R: recno input function */ int (*bt_irec) __P((struct _btree *, recno_t)); FILE *bt_rfp; /* R: record FILE pointer */ int bt_rfd; /* R: record file descriptor */ caddr_t bt_cmap; /* R: current point in mapped space */ caddr_t bt_smap; /* R: start of mapped space */ caddr_t bt_emap; /* R: end of mapped space */ size_t bt_msize; /* R: size of mapped region. */ recno_t bt_nrecs; /* R: number of records */ size_t bt_reclen; /* R: fixed record length */ u_char bt_bval; /* R: delimiting byte/pad character */ /* * NB: * B_NODUPS and R_RECNO are stored on disk, and may not be changed. */ #define B_INMEM 0x00001 /* in-memory tree */ #define B_METADIRTY 0x00002 /* need to write metadata */ #define B_MODIFIED 0x00004 /* tree modified */ #define B_NEEDSWAP 0x00008 /* if byte order requires swapping */ #define B_RDONLY 0x00010 /* read-only tree */ #define B_NODUPS 0x00020 /* no duplicate keys permitted */ #define R_RECNO 0x00080 /* record oriented tree */ #define R_CLOSEFP 0x00040 /* opened a file pointer */ #define R_EOF 0x00100 /* end of input file reached. */ #define R_FIXLEN 0x00200 /* fixed length records */ #define R_MEMMAPPED 0x00400 /* memory mapped file. */ #define R_INMEM 0x00800 /* in-memory file */ #define R_MODIFIED 0x01000 /* modified file */ #define R_RDONLY 0x02000 /* read-only file */ #define B_DB_LOCK 0x04000 /* DB_LOCK specified. */ #define B_DB_SHMEM 0x08000 /* DB_SHMEM specified. */ #define B_DB_TXN 0x10000 /* DB_TXN specified. */ u_int32_t flags; } BTREE; #include "extern.h" #ifdef __cplusplus } #endif #endif /* !_KRB5_BTREE_H */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KRB5_BTREE_EXTERN_H #define _KRB5_BTREE_EXTERN_H #ifdef __cplusplus extern "C" { #endif /*- * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)extern.h 8.11 (Berkeley) 1/9/95 */ #define __bt_close __kdb2_bt_close #define __bt_cmp __kdb2_bt_cmp #define __bt_crsrdel __kdb2_bt_crsrdel #define __bt_defcmp __kdb2_bt_defcmp #define __bt_defpfx __kdb2_bt_defpfx #define __bt_delete __kdb2_bt_delete #define __bt_dleaf __kdb2_bt_deleaf #define __bt_fd __kdb2_bt_fd #define __bt_free __kdb2_bt_free #define __bt_get __kdb2_bt_get #define __bt_new __kdb2_bt_new #define __bt_pgin __kdb2_bt_pgin #define __bt_pgout __kdb2_bt_pgout #define __bt_push __kdb2_bt_push #define __bt_put __kdb2_bt_put #define __bt_ret __kdb2_bt_ret #define __bt_search __kdb2_bt_search #define __bt_seq __kdb2_bt_seq #define __bt_setcur __kdb2_bt_setcur #define __bt_split __kdb2_bt_split #define __bt_sync __kdb2_bt_sync #define __ovfl_delete __kdb2_ovfl_delete #define __ovfl_get __kdb2_ovfl_get #define __ovfl_put __kdb2_ovfl_put #define __bt_dnpage __kdb2_bt_dnpage #define __bt_dmpage __kdb2_bt_dmpage #define __bt_dpage __kdb2_bt_dpage #define __bt_dump __kdb2_bt_dump #define __bt_stat __kdb2_bt_stat #define bt_rcinit kdb2_bt_rcinit #define bt_rcdestroy kdb2_bt_rcdestroy #define bt_rcpush kdb2_bt_rcpush #define bt_rcpop kdb2_bt_rcpop #define bt_rcclr kdb2_bt_rcclr #define bt_rcgrowstk kdb2_bt_rcgrowstk #define bt_rseqset kdb2_bt_rseqset #define bt_rseqadv kdb2_bt_rseqadv int __bt_close __P((DB *)); int __bt_cmp __P((BTREE *, const DBT *, EPG *)); int __bt_crsrdel __P((BTREE *, EPGNO *)); int __bt_defcmp __P((const DBT *, const DBT *)); size_t __bt_defpfx __P((const DBT *, const DBT *)); int __bt_delete __P((const DB *, const DBT *, u_int)); int __bt_dleaf __P((BTREE *, const DBT *, PAGE *, u_int)); int __bt_fd __P((const DB *)); int __bt_free __P((BTREE *, PAGE *)); int __bt_get __P((const DB *, const DBT *, DBT *, u_int)); PAGE *__bt_new __P((BTREE *, db_pgno_t *)); void __bt_pgin __P((void *, db_pgno_t, void *)); void __bt_pgout __P((void *, db_pgno_t, void *)); int __bt_push __P((BTREE *, db_pgno_t, int)); int __bt_put __P((const DB *dbp, DBT *, const DBT *, u_int)); int __bt_ret __P((BTREE *, EPG *, DBT *, DBT *, DBT *, DBT *, int)); EPG *__bt_search __P((BTREE *, const DBT *, int *)); int __bt_seq __P((const DB *, DBT *, DBT *, u_int)); void __bt_setcur __P((BTREE *, db_pgno_t, u_int)); int __bt_split __P((BTREE *, PAGE *, const DBT *, const DBT *, int, size_t, u_int32_t)); int __bt_sync __P((const DB *, u_int)); int __ovfl_delete __P((BTREE *, void *)); int __ovfl_get __P((BTREE *, void *, size_t *, void **, size_t *)); int __ovfl_put __P((BTREE *, const DBT *, db_pgno_t *)); #ifdef DEBUG int __bt_dnpage __P((DB *, db_pgno_t)); void __bt_dpage __P((DB *, PAGE *)); int __bt_dmpage __P((PAGE *)); int __bt_dump __P((DB *)); #endif #ifdef STATISTICS int __bt_stat __P((DB *)); #endif #ifdef __cplusplus } #endif #endif /* !_KRB5_BTREE_EXTERN_H */ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)db.c 8.4 (Berkeley) 2/21/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include "db-int.h" DB * kdb2_dbopen(fname, flags, mode, type, openinfo) const char *fname; int flags, mode; DBTYPE type; const void *openinfo; { #define DB_FLAGS (DB_LOCK | DB_SHMEM | DB_TXN) #define USE_OPEN_FLAGS \ (O_CREAT | O_EXCL | O_EXLOCK | O_NONBLOCK | O_RDONLY | \ O_RDWR | O_SHLOCK | O_TRUNC | O_BINARY) if ((flags & ~(USE_OPEN_FLAGS | DB_FLAGS)) == 0) switch (type) { case DB_BTREE: return (__bt_open(fname, flags & USE_OPEN_FLAGS, mode, openinfo, flags & DB_FLAGS)); case DB_HASH: return (__hash_open(fname, flags & USE_OPEN_FLAGS, mode, openinfo, flags & DB_FLAGS)); case DB_RECNO: return (__rec_open(fname, flags & USE_OPEN_FLAGS, mode, openinfo, flags & DB_FLAGS)); } errno = EINVAL; return (NULL); } static int __dberr() { return (RET_ERROR); } /* * __DBPANIC -- Stop. * * Parameters: * dbp: pointer to the DB structure. */ void __dbpanic(dbp) DB *dbp; { /* The only thing that can succeed is a close. */ dbp->del = (int (*)())__dberr; dbp->fd = (int (*)())__dberr; dbp->get = (int (*)())__dberr; dbp->put = (int (*)())__dberr; dbp->seq = (int (*)())__dberr; dbp->sync = (int (*)())__dberr; } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)dbm.c 8.6 (Berkeley) 11/7/95"; #endif /* LIBC_SCCS and not lint */ #include "db-int.h" #include #include #include #include #include #include "db-ndbm.h" #include "hash.h" /* If the two size fields of datum and DBMT are not equal, then * casting between structures will result in stack garbage being * transfered. Has been observed for DEC Alpha OSF, but will handle * the general case. */ #define NEED_COPY /* * * This package provides dbm and ndbm compatible interfaces to DB. * First are the DBM routines, which call the NDBM routines, and * the NDBM routines, which call the DB routines. */ static DBM *__cur_db; static void no_open_db __P((void)); int kdb2_dbminit(file) char *file; { if (__cur_db != NULL) (void)kdb2_dbm_close(__cur_db); if ((__cur_db = kdb2_dbm_open(file, O_RDWR|O_BINARY, 0)) != NULL) return (0); if ((__cur_db = kdb2_dbm_open(file, O_RDONLY|O_BINARY, 0)) != NULL) return (0); return (-1); } datum kdb2_fetch(key) datum key; { datum item; if (__cur_db == NULL) { no_open_db(); item.dptr = 0; item.dsize = 0; return (item); } return (kdb2_dbm_fetch(__cur_db, key)); } datum kdb2_firstkey() { datum item; if (__cur_db == NULL) { no_open_db(); item.dptr = 0; item.dsize = 0; return (item); } return (kdb2_dbm_firstkey(__cur_db)); } datum kdb2_nextkey(key) datum key; { datum item; if (__cur_db == NULL) { no_open_db(); item.dptr = 0; item.dsize = 0; return (item); } return (kdb2_dbm_nextkey(__cur_db)); } int kdb2_delete(key) datum key; { if (__cur_db == NULL) { no_open_db(); return (-1); } return (kdb2_dbm_delete(__cur_db, key)); } int kdb2_store(key, dat) datum key, dat; { if (__cur_db == NULL) { no_open_db(); return (-1); } return (kdb2_dbm_store(__cur_db, key, dat, DBM_REPLACE)); } static void no_open_db() { (void)fprintf(stderr, dgettext(TEXT_DOMAIN, "dbm: no open database.\n")); } /* * Returns: * *DBM on success * NULL on failure */ DBM * kdb2_dbm_open(file, flags, mode) const char *file; int flags, mode; { HASHINFO info; char path[MAXPATHLEN]; info.bsize = 4096; info.ffactor = 40; info.nelem = 1; info.cachesize = 0; info.hash = NULL; info.lorder = 0; (void)strncpy(path, file, sizeof(path) - 1); path[sizeof(path) - 1] = '\0'; (void)strncat(path, DBM_SUFFIX, sizeof(path) - 1 - strlen(path)); return ((DBM *)__hash_open(path, flags, mode, &info, 0)); } /* * Returns: * Nothing. */ void kdb2_dbm_close(db) DBM *db; { (void)(db->close)(db); } /* * Returns: * DATUM on success * NULL on failure */ datum kdb2_dbm_fetch(db, key) DBM *db; datum key; { datum retval; int status; #ifdef NEED_COPY DBT k, r; k.data = key.dptr; k.size = key.dsize; status = (db->get)(db, &k, &r, 0); retval.dptr = r.data; retval.dsize = r.size; #else status = (db->get)(db, (DBT *)&key, (DBT *)&retval, 0); #endif if (status) { retval.dptr = NULL; retval.dsize = 0; } return (retval); } /* * Returns: * DATUM on success * NULL on failure */ datum kdb2_dbm_firstkey(db) DBM *db; { int status; datum retkey; #ifdef NEED_COPY DBT k, r; status = (db->seq)(db, &k, &r, R_FIRST); retkey.dptr = k.data; retkey.dsize = k.size; #else datum retdata; status = (db->seq)(db, (DBT *)&retkey, (DBT *)&retdata, R_FIRST); #endif if (status) retkey.dptr = NULL; return (retkey); } /* * Returns: * DATUM on success * NULL on failure */ datum kdb2_dbm_nextkey(db) DBM *db; { int status; datum retkey; #ifdef NEED_COPY DBT k, r; status = (db->seq)(db, &k, &r, R_NEXT); retkey.dptr = k.data; retkey.dsize = k.size; #else datum retdata; status = (db->seq)(db, (DBT *)&retkey, (DBT *)&retdata, R_NEXT); #endif if (status) retkey.dptr = NULL; return (retkey); } /* * Returns: * 0 on success * <0 failure */ int kdb2_dbm_delete(db, key) DBM *db; datum key; { int status; #ifdef NEED_COPY DBT k; k.data = key.dptr; k.size = key.dsize; status = (db->del)(db, &k, 0); #else status = (db->del)(db, (DBT *)&key, 0); #endif if (status) return (-1); else return (0); } /* * Returns: * 0 on success * <0 failure * 1 if DBM_INSERT and entry exists */ int kdb2_dbm_store(db, key, content, flags) DBM *db; datum key, content; int flags; { #ifdef NEED_COPY DBT k, c; k.data = key.dptr; k.size = key.dsize; c.data = content.dptr; c.size = content.dsize; return ((db->put)(db, &k, &c, (flags == DBM_INSERT) ? R_NOOVERWRITE : 0)); #else return ((db->put)(db, (DBT *)&key, (DBT *)&content, (flags == DBM_INSERT) ? R_NOOVERWRITE : 0)); #endif } int kdb2_dbm_error(db) DBM *db; { HTAB *hp; hp = (HTAB *)db->internal; return (hp->local_errno); } int kdb2_dbm_clearerr(db) DBM *db; { HTAB *hp; hp = (HTAB *)db->internal; hp->local_errno = 0; return (0); } int kdb2_dbm_dirfno(db) DBM *db; { return(((HTAB *)db->internal)->fp); } /* * Copyright 1997-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KRB5_DB2_EXTERN_H #define _KRB5_DB2_EXTERN_H #ifdef __cplusplus extern "C" { #endif /*- * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)extern.h 8.8 (Berkeley) 11/7/95 */ #define __add_bigpage __kdb2_add_bigpage #define __add_ovflpage __kdb2_add_ovflpage #define __addel __kdb2_addel #define __alloc_tmp __kdb2_alloc_tmp #define __big_delete __kdb2_big_delete #define __big_insert __kdb2_big_insert #define __big_keydata __kdb2_big_keydata #define __big_return __kdb2_big_return #define __call_hash __kdb2_call_hash #define __cursor_creat __kdb2_cursor_creat #define __delete_page __kdb2_delete_page #define __delpair __kdb2_delpair #define __expand_table __kdb2_expand_table #define __find_bigpair __kdb2_find_bigpair #define __free_ovflpage __kdb2_free_ovflpage #define __get_bigkey __kdb2_get_bigkey #define __get_buf __kdb2_get_buf #define __get_item __kdb2_get_item #define __get_item_done __kdb2_get_item_done #define __get_item_first __kdb2_get_item_first #define __get_item_next __kdb2_get_item_next #define __get_item_reset __kdb2_get_item_reset #define __get_page __kdb2_get_page #define __ibitmap __kdb2_ibitmap #define __log2 __kdb2_log2 #define __new_page __kdb2_new_page #define __pgin_routine __kdb2_pgin_routine #define __pgout_routine __kdb2_pgout_routine #define __put_buf __kdb2_put_buf #define __put_page __kdb2_put_page #define __reclaim_tmp __kdb2_reclaim_tmp #define __split_page __kdb2_split_page PAGE16 *__add_bigpage __P((HTAB *, PAGE16 *, indx_t, const u_int8_t)); PAGE16 *__add_ovflpage __P((HTAB *, PAGE16 *)); int32_t __addel __P((HTAB *, ITEM_INFO *, const DBT *, const DBT *, u_int32_t, const u_int8_t)); u_int32_t __alloc_tmp __P((HTAB*)); int32_t __big_delete __P((HTAB *, PAGE16 *, indx_t)); int32_t __big_insert __P((HTAB *, PAGE16 *, const DBT *, const DBT *)); int32_t __big_keydata __P((HTAB *, PAGE16 *, DBT *, DBT *, int32_t)); int32_t __big_return __P((HTAB *, ITEM_INFO *, DBT *, int32_t)); u_int32_t __call_hash __P((HTAB *, int8_t *, int32_t)); CURSOR *__cursor_creat __P((const DB *)); int32_t __delete_page __P((HTAB *, PAGE16 *, int32_t)); int32_t __delpair __P((HTAB *, CURSOR *, ITEM_INFO *)); int32_t __expand_table __P((HTAB *)); int32_t __find_bigpair __P((HTAB *, CURSOR *, int8_t *, int32_t)); void __free_ovflpage __P((HTAB *, PAGE16 *)); int32_t __get_bigkey __P((HTAB *, PAGE16 *, indx_t, DBT *)); PAGE16 *__get_buf __P((HTAB *, u_int32_t, int32_t)); u_int32_t __get_item __P((HTAB *, CURSOR *, DBT *, DBT *, ITEM_INFO *)); u_int32_t __get_item_done __P((HTAB *, CURSOR *)); u_int32_t __get_item_first __P((HTAB *, CURSOR *, DBT *, DBT *, ITEM_INFO *)); u_int32_t __get_item_next __P((HTAB *, CURSOR *, DBT *, DBT *, ITEM_INFO *)); u_int32_t __get_item_reset __P((HTAB *, CURSOR *)); PAGE16 *__get_page __P((HTAB *, u_int32_t, int32_t)); int32_t __ibitmap __P((HTAB *, int32_t, int32_t, int32_t)); u_int32_t __log2 __P((u_int32_t)); int32_t __new_page __P((HTAB *, u_int32_t, int32_t)); void __pgin_routine __P((void *, db_pgno_t, void *)); void __pgout_routine __P((void *, db_pgno_t, void *)); u_int32_t __put_buf __P((HTAB *, PAGE16 *, u_int32_t)); int32_t __put_page __P((HTAB *, PAGE16 *, int32_t, int32_t)); void __reclaim_tmp __P((HTAB *)); int32_t __split_page __P((HTAB *, u_int32_t, u_int32_t)); /* Default hash routine. */ extern u_int32_t (*__default_hash) __P((const void *, size_t)); #ifdef HASH_STATISTICS extern long hash_accesses, hash_bigpages, hash_collisions, hash_expansions; extern long hash_overflow; #endif #ifdef __cplusplus } #endif #endif /* !_KRB5_DB2_EXTERN_H */ /* * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #undef _TS_ERRNO_ #include #include #include #include #include #include #include #include #include #ifdef DEBUG #include #endif #include "db-int.h" #include "hash.h" #include "page.h" #include "extern.h" static int32_t flush_meta __P((HTAB *)); static int32_t hash_access __P((HTAB *, ACTION, const DBT *, DBT *)); static int32_t hash_close __P((DB *)); static int32_t hash_delete __P((const DB *, const DBT *, u_int32_t)); static int32_t hash_fd __P((const DB *)); static int32_t hash_get __P((const DB *, const DBT *, DBT *, u_int32_t)); static int32_t hash_put __P((const DB *, DBT *, const DBT *, u_int32_t)); static int32_t hash_seq __P((const DB *, DBT *, DBT *, u_int32_t)); static int32_t hash_sync __P((const DB *, u_int32_t)); static int32_t hdestroy __P((HTAB *)); static int32_t cursor_get __P((const DB *, CURSOR *, DBT *, DBT *, \ u_int32_t)); static int32_t cursor_delete __P((const DB *, CURSOR *, u_int32_t)); static HTAB *init_hash __P((HTAB *, const char *, const HASHINFO *)); static int32_t init_htab __P((HTAB *, int32_t)); #if DB_BYTE_ORDER == DB_LITTLE_ENDIAN static void swap_header __P((HTAB *)); static void swap_header_copy __P((HASHHDR *, HASHHDR *)); #endif static u_int32_t hget_header __P((HTAB *, u_int32_t)); static void hput_header __P((HTAB *)); #define RETURN_ERROR(ERR, LOC) { save_errno = ERR; goto LOC; } /* Return values */ #define SUCCESS (0) #define ERROR (-1) #define ABNORMAL (1) #ifdef HASH_STATISTICS u_int32_t hash_accesses, hash_collisions, hash_expansions, hash_overflows, hash_bigpages; #endif /************************** INTERFACE ROUTINES ***************************/ /* OPEN/CLOSE */ extern DB * __kdb2_hash_open(file, flags, mode, info, dflags) const char *file; int32_t flags, mode, dflags; const HASHINFO *info; /* Special directives for create */ { struct stat statbuf; DB *dbp; DBT mpool_key; HTAB *hashp; int32_t bpages, csize, new_table, save_errno, specified_file; if ((flags & O_ACCMODE) == O_WRONLY) { errno = EINVAL; return (NULL); } if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB)))) return (NULL); hashp->fp = -1; /* set this now, before file goes away... */ specified_file = (file != NULL); if (!file) { /* * If we are root and thus have access to /var/run, then use * it, else tempnam(3) will use /var/tmp. */ if (!(file = tempnam("/var/run", NULL))) { /* * No memory here is not the only failure * possibility, but probably the most likely. */ errno = ENOMEM; free(hashp); return(NULL); } /* store the file name so that we can unlink it later */ hashp->fname = file; #ifdef DEBUG fprintf(stderr, dgettext(TEXT_DOMAIN, "Using file name %s.\n"), file); #endif } /* * Even if user wants write only, we need to be able to read * the actual file, so we need to open it read/write. But, the * field in the hashp structure needs to be accurate so that * we can check accesses. */ hashp->flags = flags; hashp->save_file = specified_file && (hashp->flags & O_RDWR); new_table = 0; if (!file || (flags & O_TRUNC) || (stat(file, &statbuf) && (errno == ENOENT))) { if (errno == ENOENT) errno = 0; /* In case someone looks at errno. */ new_table = 1; } if (file) { if ((hashp->fp = open(file, flags|O_BINARY, mode)) == -1) RETURN_ERROR(errno, error0); (void)fcntl(hashp->fp, F_SETFD, 1); } /* Process arguments to set up hash table header. */ if (new_table) { if (!(hashp = init_hash(hashp, file, info))) RETURN_ERROR(errno, error1); } else { /* Table already exists */ if (info && info->hash) hashp->hash = info->hash; else hashp->hash = __default_hash; /* copy metadata from page into header */ if (hget_header(hashp, (info && info->bsize ? info->bsize : DEF_BUCKET_SIZE)) != sizeof(HASHHDR)) RETURN_ERROR(EFTYPE, error1); /* Verify file type, versions and hash function */ if (hashp->hdr.magic != HASHMAGIC) RETURN_ERROR(EFTYPE, error1); #define OLDHASHVERSION 1 if (hashp->hdr.version != HASHVERSION && hashp->hdr.version != OLDHASHVERSION) RETURN_ERROR(EFTYPE, error1); if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->hdr.h_charkey) RETURN_ERROR(EFTYPE, error1); /* * Figure out how many segments we need. Max_Bucket is the * maximum bucket number, so the number of buckets is * max_bucket + 1. */ /* Read in bitmaps */ bpages = (hashp->hdr.spares[hashp->hdr.ovfl_point] + (hashp->hdr.bsize << BYTE_SHIFT) - 1) >> (hashp->hdr.bshift + BYTE_SHIFT); hashp->nmaps = bpages; (void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_int32_t *)); } /* start up mpool */ mpool_key.data = (u_int8_t *)file; mpool_key.size = strlen(file); if (info && info->cachesize) csize = info->cachesize / hashp->hdr.bsize; else csize = DEF_CACHESIZE / hashp->hdr.bsize; hashp->mp = mpool_open(&mpool_key, hashp->fp, hashp->hdr.bsize, csize); if (!hashp->mp) RETURN_ERROR(errno, error1); mpool_filter(hashp->mp, __pgin_routine, __pgout_routine, hashp); /* * For a new table, set up the bitmaps. */ if (new_table && init_htab(hashp, info && info->nelem ? info->nelem : 1)) goto error2; /* initialize the cursor queue */ TAILQ_INIT(&hashp->curs_queue); hashp->seq_cursor = NULL; /* get a chunk of memory for our split buffer */ hashp->split_buf = (PAGE16 *)malloc(hashp->hdr.bsize); if (!hashp->split_buf) goto error2; hashp->new_file = new_table; if (!(dbp = (DB *)malloc(sizeof(DB)))) goto error2; dbp->internal = hashp; dbp->close = hash_close; dbp->del = hash_delete; dbp->fd = hash_fd; dbp->get = hash_get; dbp->put = hash_put; dbp->seq = hash_seq; dbp->sync = hash_sync; dbp->type = DB_HASH; #ifdef DEBUG (void)fprintf(stderr, "%s\n%s%lx\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n", "init_htab:", "TABLE POINTER ", (void *)hashp, "BUCKET SIZE ", hashp->hdr.bsize, "BUCKET SHIFT ", hashp->hdr.bshift, "FILL FACTOR ", hashp->hdr.ffactor, "MAX BUCKET ", hashp->hdr.max_bucket, "OVFL POINT ", hashp->hdr.ovfl_point, "LAST FREED ", hashp->hdr.last_freed, "HIGH MASK ", hashp->hdr.high_mask, "LOW MASK ", hashp->hdr.low_mask, "NKEYS ", hashp->hdr.nkeys); #endif #ifdef HASH_STATISTICS hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; hash_bigpages = 0; #endif return (dbp); error2: save_errno = errno; hdestroy(hashp); errno = save_errno; return (NULL); error1: if (hashp != NULL) (void)close(hashp->fp); error0: if (!specified_file) free((void*)(hashp->fname)); /* SUNW14resync */ free(hashp); errno = save_errno; return (NULL); } static int32_t hash_close(dbp) DB *dbp; { HTAB *hashp; int32_t retval; if (!dbp) return (ERROR); hashp = (HTAB *)dbp->internal; retval = hdestroy(hashp); free(dbp); return (retval); } static int32_t hash_fd(dbp) const DB *dbp; { HTAB *hashp; if (!dbp) return (ERROR); hashp = (HTAB *)dbp->internal; if (hashp->fp == -1) { errno = ENOENT; return (-1); } return (hashp->fp); } /************************** LOCAL CREATION ROUTINES **********************/ static HTAB * init_hash(hashp, file, info) HTAB *hashp; const char *file; const HASHINFO *info; { struct stat statbuf; hashp->hdr.nkeys = 0; hashp->hdr.lorder = DB_BYTE_ORDER; hashp->hdr.bsize = DEF_BUCKET_SIZE; hashp->hdr.bshift = DEF_BUCKET_SHIFT; hashp->hdr.ffactor = DEF_FFACTOR; hashp->hash = __default_hash; memset(hashp->hdr.spares, 0, sizeof(hashp->hdr.spares)); memset(hashp->hdr.bitmaps, 0, sizeof(hashp->hdr.bitmaps)); /* Fix bucket size to be optimal for file system */ if (file != NULL) { if (stat(file, &statbuf)) return (NULL); hashp->hdr.bsize = statbuf.st_blksize; hashp->hdr.bshift = __log2(hashp->hdr.bsize); } if (info) { if (info->bsize) { /* Round pagesize up to power of 2 */ hashp->hdr.bshift = __log2(info->bsize); hashp->hdr.bsize = 1 << hashp->hdr.bshift; if (hashp->hdr.bsize > MAX_BSIZE) { errno = EINVAL; return (NULL); } } if (info->ffactor) hashp->hdr.ffactor = info->ffactor; if (info->hash) hashp->hash = info->hash; if (info->lorder) { if ((info->lorder != DB_BIG_ENDIAN) && (info->lorder != DB_LITTLE_ENDIAN)) { errno = EINVAL; return (NULL); } hashp->hdr.lorder = info->lorder; } } return (hashp); } /* * Returns 0 on No Error */ static int32_t init_htab(hashp, nelem) HTAB *hashp; int32_t nelem; { int32_t l2, nbuckets; /* * Divide number of elements by the fill factor and determine a * desired number of buckets. Allocate space for the next greater * power of two number of buckets. */ nelem = (nelem - 1) / hashp->hdr.ffactor + 1; l2 = __log2(MAX(nelem, 2)); nbuckets = 1 << l2; hashp->hdr.spares[l2] = l2 + 1; hashp->hdr.spares[l2 + 1] = l2 + 1; hashp->hdr.ovfl_point = l2; hashp->hdr.last_freed = 2; hashp->hdr.max_bucket = hashp->hdr.low_mask = nbuckets - 1; hashp->hdr.high_mask = (nbuckets << 1) - 1; /* * The number of header pages is the size of the header divided by * the amount of freespace on header pages (the page size - the * size of 1 integer where the length of the header info on that * page is stored) plus another page if it didn't divide evenly. */ hashp->hdr.hdrpages = (sizeof(HASHHDR) / (hashp->hdr.bsize - HEADER_OVERHEAD)) + (((sizeof(HASHHDR) % (hashp->hdr.bsize - HEADER_OVERHEAD)) == 0) ? 0 : 1); /* Create pages for these buckets */ /* for (i = 0; i <= hashp->hdr.max_bucket; i++) { if (__new_page(hashp, (u_int32_t)i, A_BUCKET) != 0) return (-1); } */ /* First bitmap page is at: splitpoint l2 page offset 1 */ if (__ibitmap(hashp, OADDR_OF(l2, 1), l2 + 1, 0)) return (-1); return (0); } /* * Functions to get/put hash header. We access the file directly. */ static u_int32_t hget_header(hashp, page_size) HTAB *hashp; u_int32_t page_size; { u_int32_t num_copied; u_int8_t *hdr_dest; num_copied = 0; hdr_dest = (u_int8_t *)&hashp->hdr; /* * XXX * This should not be printing to stderr on a "normal" error case. */ lseek(hashp->fp, 0, SEEK_SET); num_copied = read(hashp->fp, hdr_dest, sizeof(HASHHDR)); if (num_copied != sizeof(HASHHDR)) { /* Solaris Kerberos: Make sure to print a newline */ fprintf(stderr, dgettext(TEXT_DOMAIN, "hash: could not retrieve header\n")); return (0); } #if DB_BYTE_ORDER == DB_LITTLE_ENDIAN swap_header(hashp); #endif return (num_copied); } static void hput_header(hashp) HTAB *hashp; { HASHHDR *whdrp; #if DB_BYTE_ORDER == DB_LITTLE_ENDIAN HASHHDR whdr; #endif u_int32_t num_copied, i; num_copied = i = 0; whdrp = &hashp->hdr; #if DB_BYTE_ORDER == DB_LITTLE_ENDIAN whdrp = &whdr; swap_header_copy(&hashp->hdr, whdrp); #endif lseek(hashp->fp, 0, SEEK_SET); num_copied = write(hashp->fp, whdrp, sizeof(HASHHDR)); if (num_copied != sizeof(HASHHDR)) /* Solaris Kerberos: Make sure to print a newline */ (void)fprintf(stderr, dgettext(TEXT_DOMAIN, "hash: could not write hash header\n")); return; } /********************** DESTROY/CLOSE ROUTINES ************************/ /* * Flushes any changes to the file if necessary and destroys the hashp * structure, freeing all allocated space. */ static int32_t hdestroy(hashp) HTAB *hashp; { int32_t save_errno; save_errno = 0; #ifdef HASH_STATISTICS { int i; (void)fprintf(stderr, dgettext(TEXT_DOMAIN, "hdestroy: accesses %ld collisions %ld\n"), hash_accesses, hash_collisions); (void)fprintf(stderr, dgettext(TEXT_DOMAIN, "hdestroy: expansions %ld\n"), hash_expansions); (void)fprintf(stderr, dgettext(TEXT_DOMAIN, "hdestroy: overflows %ld\n"), hash_overflows); (void)fprintf(stderr, dgettext(TEXT_DOMAIN, "hdestroy: big key/data pages %ld\n"), hash_bigpages); (void)fprintf(stderr, "keys %ld maxp %d\n", hashp->hdr.nkeys, hashp->hdr.max_bucket); for (i = 0; i < NCACHED; i++) (void)fprintf(stderr, "spares[%d] = %d\n", i, hashp->hdr.spares[i]); } #endif if (flush_meta(hashp) && !save_errno) save_errno = errno; /* Free the split page */ if (hashp->split_buf) free(hashp->split_buf); /* Free the big key and big data returns */ if (hashp->bigkey_buf) free(hashp->bigkey_buf); if (hashp->bigdata_buf) free(hashp->bigdata_buf); /* XXX This should really iterate over the cursor queue, but it's not clear how to do that, and the only cursor a hash table ever creates is the one used by hash_seq(). Passing NULL as the first arg is also a kludge, but I know that it's never used, so I do it. The intent is to plug the memory leak. Correctness can come later. */ if (hashp->seq_cursor) hashp->seq_cursor->delete(NULL, hashp->seq_cursor, 0); /* shut down mpool */ mpool_sync(hashp->mp); mpool_close(hashp->mp); if (hashp->fp != -1) (void)close(hashp->fp); /* * *** This may cause problems if hashp->fname is set in any case * other than the case that we are generating a temporary file name. * Note that the new version of mpool should support temporary * files within mpool itself. */ if (hashp->fname && !hashp->save_file) { #ifdef DEBUG fprintf(stderr, dgettext(TEXT_DOMAIN, "Unlinking file %s.\n"), hashp->fname); #endif /* we need to chmod the file to allow it to be deleted... */ chmod(hashp->fname, 0700); unlink(hashp->fname); /* destroy the temporary name */ free((void *)(hashp->fname)); /* SUNW14resync */ } free(hashp); if (save_errno) { errno = save_errno; return (ERROR); } return (SUCCESS); } /* * Write modified pages to disk * * Returns: * 0 == OK * -1 ERROR */ static int32_t hash_sync(dbp, flags) const DB *dbp; u_int32_t flags; { HTAB *hashp; hashp = (HTAB *)dbp->internal; /* * XXX * Check success/failure conditions. */ return (flush_meta(hashp) || mpool_sync(hashp->mp)); } /* * Returns: * 0 == OK * -1 indicates that errno should be set */ static int32_t flush_meta(hashp) HTAB *hashp; { int32_t i; if (!hashp->save_file) return (0); hashp->hdr.magic = HASHMAGIC; hashp->hdr.version = HASHVERSION; hashp->hdr.h_charkey = hashp->hash(CHARKEY, sizeof(CHARKEY)); /* write out metadata */ hput_header(hashp); for (i = 0; i < NCACHED; i++) if (hashp->mapp[i]) { if (__put_page(hashp, (PAGE16 *)hashp->mapp[i], A_BITMAP, 1)) return (-1); hashp->mapp[i] = NULL; } return (0); } /*******************************SEARCH ROUTINES *****************************/ /* * All the access routines return * * Returns: * 0 on SUCCESS * 1 to indicate an external ERROR (i.e. key not found, etc) * -1 to indicate an internal ERROR (i.e. out of memory, etc) */ /* *** make sure this is true! */ static int32_t hash_get(dbp, key, data, flag) const DB *dbp; const DBT *key; DBT *data; u_int32_t flag; { HTAB *hashp; hashp = (HTAB *)dbp->internal; if (flag) { hashp->local_errno = errno = EINVAL; return (ERROR); } return (hash_access(hashp, HASH_GET, key, data)); } static int32_t hash_put(dbp, key, data, flag) const DB *dbp; DBT *key; const DBT *data; u_int32_t flag; { HTAB *hashp; hashp = (HTAB *)dbp->internal; if (flag && flag != R_NOOVERWRITE) { hashp->local_errno = errno = EINVAL; return (ERROR); } if ((hashp->flags & O_ACCMODE) == O_RDONLY) { hashp->local_errno = errno = EPERM; return (ERROR); } return (hash_access(hashp, flag == R_NOOVERWRITE ? HASH_PUTNEW : HASH_PUT, key, (DBT *)data)); } static int32_t hash_delete(dbp, key, flag) const DB *dbp; const DBT *key; u_int32_t flag; /* Ignored */ { HTAB *hashp; hashp = (HTAB *)dbp->internal; if (flag) { hashp->local_errno = errno = EINVAL; return (ERROR); } if ((hashp->flags & O_ACCMODE) == O_RDONLY) { hashp->local_errno = errno = EPERM; return (ERROR); } return (hash_access(hashp, HASH_DELETE, key, NULL)); } /* * Assume that hashp has been set in wrapper routine. */ static int32_t hash_access(hashp, action, key, val) HTAB *hashp; ACTION action; const DBT *key; DBT *val; { DBT page_key, page_val; CURSOR cursor; ITEM_INFO item_info; u_int32_t bucket; u_int32_t num_items; #ifdef HASH_STATISTICS hash_accesses++; #endif num_items = 0; /* * Set up item_info so that we're looking for space to add an item * as we cycle through the pages looking for the key. */ if (action == HASH_PUT || action == HASH_PUTNEW) { if (ISBIG(key->size + val->size, hashp)) item_info.seek_size = PAIR_OVERHEAD; else item_info.seek_size = key->size + val->size; } else item_info.seek_size = 0; item_info.seek_found_page = 0; bucket = __call_hash(hashp, (int8_t *)key->data, key->size); cursor.pagep = NULL; __get_item_reset(hashp, &cursor); cursor.bucket = bucket; while (1) { __get_item_next(hashp, &cursor, &page_key, &page_val, &item_info); if (item_info.status == ITEM_ERROR) return (ABNORMAL); if (item_info.status == ITEM_NO_MORE) break; num_items++; if (item_info.key_off == BIGPAIR) { /* * !!! * 0 is a valid index. */ if (__find_bigpair(hashp, &cursor, (int8_t *)key->data, key->size) > 0) goto found; } else if (key->size == page_key.size && !memcmp(key->data, page_key.data, key->size)) goto found; } #ifdef HASH_STATISTICS hash_collisions++; #endif __get_item_done(hashp, &cursor); /* * At this point, item_info will list either the last page in * the chain, or the last page in the chain plus a pgno for where * to find the first page in the chain with space for the * item we wish to add. */ /* Not found */ switch (action) { case HASH_PUT: case HASH_PUTNEW: if (__addel(hashp, &item_info, key, val, num_items, 0)) return (ERROR); break; case HASH_GET: case HASH_DELETE: default: return (ABNORMAL); } if (item_info.caused_expand) __expand_table(hashp); return (SUCCESS); found: __get_item_done(hashp, &cursor); switch (action) { case HASH_PUTNEW: /* mpool_put(hashp->mp, pagep, 0); */ return (ABNORMAL); case HASH_GET: if (item_info.key_off == BIGPAIR) { if (__big_return(hashp, &item_info, val, 0)) return (ERROR); } else { val->data = page_val.data; val->size = page_val.size; } /* *** data may not be available! */ break; case HASH_PUT: if (__delpair(hashp, &cursor, &item_info) || __addel(hashp, &item_info, key, val, UNKNOWN, 0)) return (ERROR); __get_item_done(hashp, &cursor); if (item_info.caused_expand) __expand_table(hashp); break; case HASH_DELETE: if (__delpair(hashp, &cursor, &item_info)) return (ERROR); break; default: abort(); } return (SUCCESS); } /* ****************** CURSORS ********************************** */ CURSOR * __cursor_creat(dbp) const DB *dbp; { CURSOR *new_curs; HTAB *hashp; new_curs = (CURSOR *)malloc(sizeof(struct cursor_t)); if (!new_curs) return NULL; new_curs->internal = (struct item_info *)malloc(sizeof(struct item_info)); if (!new_curs->internal) { free(new_curs); return NULL; } new_curs->get = cursor_get; new_curs->delete = cursor_delete; new_curs->bucket = 0; new_curs->pgno = INVALID_PGNO; new_curs->ndx = 0; new_curs->pgndx = 0; new_curs->pagep = NULL; /* place onto queue of cursors */ hashp = (HTAB *)dbp->internal; TAILQ_INSERT_TAIL(&hashp->curs_queue, new_curs, queue); return new_curs; } static int32_t cursor_get(dbp, cursorp, key, val, flags) const DB *dbp; CURSOR *cursorp; DBT *key, *val; u_int32_t flags; { HTAB *hashp; ITEM_INFO item_info; hashp = (HTAB *)dbp->internal; if (flags && flags != R_FIRST && flags != R_NEXT) { hashp->local_errno = errno = EINVAL; return (ERROR); } #ifdef HASH_STATISTICS hash_accesses++; #endif item_info.seek_size = 0; if (flags == R_FIRST) __get_item_first(hashp, cursorp, key, val, &item_info); else __get_item_next(hashp, cursorp, key, val, &item_info); /* * This needs to be changed around. As is, get_item_next advances * the pointers on the page but this function actually advances * bucket pointers. This works, since the only other place we * use get_item_next is in hash_access which only deals with one * bucket at a time. However, there is the problem that certain other * functions (such as find_bigpair and delpair) depend on the * pgndx member of the cursor. Right now, they are using pngdx - 1 * since indices refer to the __next__ item that is to be fetched * from the page. This is ugly, as you may have noticed, whoever * you are. The best solution would be to depend on item_infos to * deal with _current_ information, and have the cursors only * deal with _next_ information. In that scheme, get_item_next * would also advance buckets. Version 3... */ /* * Must always enter this loop to do error handling and * check for big key/data pair. */ while (1) { if (item_info.status == ITEM_OK) { if (item_info.key_off == BIGPAIR && __big_keydata(hashp, cursorp->pagep, key, val, item_info.pgndx)) return (ABNORMAL); break; } else if (item_info.status != ITEM_NO_MORE) return (ABNORMAL); __put_page(hashp, cursorp->pagep, A_RAW, 0); cursorp->ndx = cursorp->pgndx = 0; cursorp->bucket++; cursorp->pgno = INVALID_PGNO; cursorp->pagep = NULL; if (cursorp->bucket > hashp->hdr.max_bucket) return (ABNORMAL); __get_item_next(hashp, cursorp, key, val, &item_info); } __get_item_done(hashp, cursorp); return (0); } static int32_t cursor_delete(dbp, cursor, flags) const DB *dbp; CURSOR *cursor; u_int32_t flags; { /* XXX this is empirically determined, so it might not be completely correct, but it seems to work. At the very least it fixes a memory leak */ free(cursor->internal); free(cursor); return (0); } static int32_t hash_seq(dbp, key, val, flag) const DB *dbp; DBT *key, *val; u_int32_t flag; { HTAB *hashp; /* * Seq just uses the default cursor to go sequecing through the * database. Note that the default cursor is the first in the list. */ hashp = (HTAB *)dbp->internal; if (!hashp->seq_cursor) hashp->seq_cursor = __cursor_creat(dbp); return (hashp->seq_cursor->get(dbp, hashp->seq_cursor, key, val, flag)); } /********************************* UTILITIES ************************/ /* * Returns: * 0 ==> OK * -1 ==> Error */ int32_t __expand_table(hashp) HTAB *hashp; { u_int32_t old_bucket, new_bucket; int32_t spare_ndx; #ifdef HASH_STATISTICS hash_expansions++; #endif new_bucket = ++hashp->hdr.max_bucket; old_bucket = (hashp->hdr.max_bucket & hashp->hdr.low_mask); /* Get a page for this new bucket */ if (__new_page(hashp, new_bucket, A_BUCKET) != 0) return (-1); /* * If the split point is increasing (hdr.max_bucket's log base 2 * increases), we need to copy the current contents of the spare * split bucket to the next bucket. */ spare_ndx = __log2(hashp->hdr.max_bucket + 1); if (spare_ndx > hashp->hdr.ovfl_point) { hashp->hdr.spares[spare_ndx] = hashp->hdr.spares[hashp->hdr.ovfl_point]; hashp->hdr.ovfl_point = spare_ndx; } if (new_bucket > hashp->hdr.high_mask) { /* Starting a new doubling */ hashp->hdr.low_mask = hashp->hdr.high_mask; hashp->hdr.high_mask = new_bucket | hashp->hdr.low_mask; } if (BUCKET_TO_PAGE(new_bucket) > MAX_PAGES(hashp)) { fprintf(stderr,dgettext(TEXT_DOMAIN, "hash: Cannot allocate new bucket. Pages exhausted.\n")); return (-1); } /* Relocate records to the new bucket */ return (__split_page(hashp, old_bucket, new_bucket)); } u_int32_t __call_hash(hashp, k, len) HTAB *hashp; int8_t *k; int32_t len; { int32_t n, bucket; n = hashp->hash(k, len); bucket = n & hashp->hdr.high_mask; if (bucket > hashp->hdr.max_bucket) bucket = bucket & hashp->hdr.low_mask; return (bucket); } #if DB_BYTE_ORDER == DB_LITTLE_ENDIAN /* * Hashp->hdr needs to be byteswapped. */ static void swap_header_copy(srcp, destp) HASHHDR *srcp, *destp; { int32_t i; P_32_COPY(srcp->magic, destp->magic); P_32_COPY(srcp->version, destp->version); P_32_COPY(srcp->lorder, destp->lorder); P_32_COPY(srcp->bsize, destp->bsize); P_32_COPY(srcp->bshift, destp->bshift); P_32_COPY(srcp->ovfl_point, destp->ovfl_point); P_32_COPY(srcp->last_freed, destp->last_freed); P_32_COPY(srcp->max_bucket, destp->max_bucket); P_32_COPY(srcp->high_mask, destp->high_mask); P_32_COPY(srcp->low_mask, destp->low_mask); P_32_COPY(srcp->ffactor, destp->ffactor); P_32_COPY(srcp->nkeys, destp->nkeys); P_32_COPY(srcp->hdrpages, destp->hdrpages); P_32_COPY(srcp->h_charkey, destp->h_charkey); for (i = 0; i < NCACHED; i++) { P_32_COPY(srcp->spares[i], destp->spares[i]); P_16_COPY(srcp->bitmaps[i], destp->bitmaps[i]); } } static void swap_header(hashp) HTAB *hashp; { HASHHDR *hdrp; int32_t i; hdrp = &hashp->hdr; M_32_SWAP(hdrp->magic); M_32_SWAP(hdrp->version); M_32_SWAP(hdrp->lorder); M_32_SWAP(hdrp->bsize); M_32_SWAP(hdrp->bshift); M_32_SWAP(hdrp->ovfl_point); M_32_SWAP(hdrp->last_freed); M_32_SWAP(hdrp->max_bucket); M_32_SWAP(hdrp->high_mask); M_32_SWAP(hdrp->low_mask); M_32_SWAP(hdrp->ffactor); M_32_SWAP(hdrp->nkeys); M_32_SWAP(hdrp->hdrpages); M_32_SWAP(hdrp->h_charkey); for (i = 0; i < NCACHED; i++) { M_32_SWAP(hdrp->spares[i]); M_16_SWAP(hdrp->bitmaps[i]); } } #endif /* DB_BYTE_ORDER == DB_LITTLE_ENDIAN */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KRB5_DB2_HASH_H #define _KRB5_DB2_HASH_H #ifdef __cplusplus extern "C" { #endif /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)hash.h 8.4 (Berkeley) 11/2/95 */ #include "mpool.h" #include "db-queue.h" /* Operations */ typedef enum { HASH_GET, HASH_PUT, HASH_PUTNEW, HASH_DELETE, HASH_FIRST, HASH_NEXT } ACTION; /* cursor structure */ typedef struct cursor_t { TAILQ_ENTRY(cursor_t) queue; int (*get) __P((const DB *, struct cursor_t *, DBT *, DBT *, \ u_int32_t)); int (*delete) __P((const DB *, struct cursor_t *, u_int32_t)); db_pgno_t bucket; db_pgno_t pgno; indx_t ndx; indx_t pgndx; u_int16_t *pagep; void *internal; } CURSOR; #define IS_BUCKET(X) ((X) & BUF_BUCKET) #define IS_VALID(X) (!((X) & BUF_INVALID)) /* Hash Table Information */ typedef struct hashhdr { /* Disk resident portion */ int32_t magic; /* Magic NO for hash tables */ int32_t version; /* Version ID */ int32_t lorder; /* Byte Order */ int32_t bsize; /* Bucket/Page Size */ int32_t bshift; /* Bucket shift */ int32_t ovfl_point; /* Where overflow pages are being allocated */ int32_t last_freed; /* Last overflow page freed */ int32_t max_bucket; /* ID of Maximum bucket in use */ int32_t high_mask; /* Mask to modulo into entire table */ int32_t low_mask; /* Mask to modulo into lower half of table */ int32_t ffactor; /* Fill factor */ int32_t nkeys; /* Number of keys in hash table */ int32_t hdrpages; /* Size of table header */ int32_t h_charkey; /* value of hash(CHARKEY) */ #define NCACHED 32 /* number of bit maps and spare points */ int32_t spares[NCACHED];/* spare pages for overflow */ u_int16_t bitmaps[NCACHED]; /* address of overflow page bitmaps */ } HASHHDR; typedef struct htab { /* Memory resident data structure */ TAILQ_HEAD(_cursor_queue, cursor_t) curs_queue; HASHHDR hdr; /* Header */ u_int32_t (*hash) __P((const void *, size_t)); /* Hash Function */ int32_t flags; /* Flag values */ int32_t fp; /* File pointer */ const char *fname; /* File path */ u_int8_t *bigdata_buf; /* Temporary Buffer for BIG data */ u_int8_t *bigkey_buf; /* Temporary Buffer for BIG keys */ u_int16_t *split_buf; /* Temporary buffer for splits */ CURSOR *seq_cursor; /* Cursor used for hash_seq */ int32_t local_errno; /* Error Number -- for DBM compatability */ int32_t new_file; /* Indicates if fd is backing store or no */ int32_t save_file; /* Indicates whether we need to flush file at * exit */ u_int32_t *mapp[NCACHED];/* Pointers to page maps */ int32_t nmaps; /* Initial number of bitmaps */ MPOOL *mp; /* mpool for buffer management */ } HTAB; /* * Constants */ #define MAX_BSIZE 65536 /* 2^16 */ #define MIN_BUFFERS 6 #define MINHDRSIZE 512 #define DEF_CACHESIZE 65536 #define DEF_BUCKET_SHIFT 12 /* log2(BUCKET) */ #define DEF_BUCKET_SIZE (1<> SPLITSHIFT) #define OPAGENUM(N) ((N) & SPLITMASK) #define OADDR_OF(S,O) ((u_int32_t)((u_int32_t)(S) << SPLITSHIFT) + (O)) #define BUCKET_TO_PAGE(B) \ ((B) + hashp->hdr.hdrpages + ((B) \ ? hashp->hdr.spares[__log2((B)+1)-1] : 0)) #define OADDR_TO_PAGE(B) \ (BUCKET_TO_PAGE ( (1 << SPLITNUM((B))) -1 ) + OPAGENUM((B))) #define POW2(N) (1 << (N)) #define MAX_PAGES(H) (DB_OFF_T_MAX / (H)->hdr.bsize) /* Shorthands for accessing structure */ #define METADATA_PGNO 0 #define SPLIT_PGNO 0xFFFF typedef struct item_info { db_pgno_t pgno; db_pgno_t bucket; indx_t ndx; indx_t pgndx; u_int8_t status; int32_t seek_size; db_pgno_t seek_found_page; indx_t key_off; indx_t data_off; u_int8_t caused_expand; } ITEM_INFO; #define ITEM_ERROR 0 #define ITEM_OK 1 #define ITEM_NO_MORE 2 #define ITEM_GET_FIRST 0 #define ITEM_GET_NEXT 1 #define ITEM_GET_RESET 2 #define ITEM_GET_DONE 3 #define ITEM_GET_N 4 #define UNKNOWN 0xffffffff /* for num_items */ #define NO_EXPAND 0xfffffffe #ifdef __cplusplus } #endif #endif /* !_KRB5_DB2_HASH_H */ /* * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ /* * PACKAGE: hash * DESCRIPTION: * Big key/data handling for the hashing package. * * ROUTINES: * External * __big_keydata * __big_split * __big_insert * __big_return * __big_delete * __find_last_page * Internal * collect_key * collect_data */ #include #include #include #ifdef DEBUG #include #endif #include "db-int.h" #include "hash.h" #include "page.h" #include "extern.h" static int32_t collect_key __P((HTAB *, PAGE16 *, int32_t, db_pgno_t *)); static int32_t collect_data __P((HTAB *, PAGE16 *, int32_t)); /* * Big_insert * * You need to do an insert and the key/data pair is greater than * MINFILL * the bucket size * * Returns: * 0 ==> OK * -1 ==> ERROR */ int32_t __big_insert(hashp, pagep, key, val) HTAB *hashp; PAGE16 *pagep; const DBT *key, *val; { size_t key_size, val_size; indx_t key_move_bytes, val_move_bytes; int8_t *key_data, *val_data, base_page; key_data = (int8_t *)key->data; key_size = key->size; val_data = (int8_t *)val->data; val_size = val->size; NUM_ENT(pagep) = NUM_ENT(pagep) + 1; for (base_page = 1; key_size + val_size;) { /* Add a page! */ pagep = __add_bigpage(hashp, pagep, NUM_ENT(pagep) - 1, base_page); if (!pagep) return (-1); /* There's just going to be one entry on this page. */ NUM_ENT(pagep) = 1; /* Move the key's data. */ key_move_bytes = MIN(FREESPACE(pagep), key_size); /* Mark the page as to how much key & data is on this page. */ BIGKEYLEN(pagep) = key_move_bytes; val_move_bytes = MIN(FREESPACE(pagep) - key_move_bytes, val_size); BIGDATALEN(pagep) = val_move_bytes; /* Note big pages build beginning --> end, not vice versa. */ if (key_move_bytes) memmove(BIGKEY(pagep), key_data, key_move_bytes); if (val_move_bytes) memmove(BIGDATA(pagep), val_data, val_move_bytes); key_size -= key_move_bytes; key_data += key_move_bytes; val_size -= val_move_bytes; val_data += val_move_bytes; base_page = 0; } __put_page(hashp, pagep, A_RAW, 1); return (0); } /* * Called when we need to delete a big pair. * * Returns: * 0 => OK * -1 => ERROR */ int32_t #ifdef __STDC__ __big_delete(HTAB *hashp, PAGE16 *pagep, indx_t ndx) #else __big_delete(hashp, pagep, ndx) HTAB *hashp; PAGE16 *pagep; u_int32_t ndx; /* Index of big pair on base page. */ #endif { PAGE16 *last_pagep; /* Get first page with big key/data. */ pagep = __get_page(hashp, OADDR_TO_PAGE(DATA_OFF(pagep, ndx)), A_RAW); if (!pagep) return (-1); /* * Traverse through the pages, freeing the previous one (except * the first) at each new page. */ while (NEXT_PGNO(pagep) != INVALID_PGNO) { last_pagep = pagep; pagep = __get_page(hashp, NEXT_PGNO(pagep), A_RAW); if (!pagep) return (-1); __delete_page(hashp, last_pagep, A_OVFL); } /* Free the last page in the chain. */ __delete_page(hashp, pagep, A_OVFL); return (0); } /* * Given a key, indicates whether the big key at cursorp matches the * given key. * * Returns: * 1 = Found! * 0 = Key not found * -1 error */ int32_t __find_bigpair(hashp, cursorp, key, size) HTAB *hashp; CURSOR *cursorp; int8_t *key; int32_t size; { PAGE16 *pagep, *hold_pagep; db_pgno_t next_pgno; int32_t ksize; int8_t *kkey; ksize = size; kkey = key; hold_pagep = NULL; /* Chances are, hashp->cpage is the base page. */ if (cursorp->pagep) pagep = hold_pagep = cursorp->pagep; else { pagep = __get_page(hashp, cursorp->pgno, A_RAW); if (!pagep) return (-1); } /* * Now, get the first page with the big stuff on it. * * XXX * KLUDGE: we know that cursor is looking at the _next_ item, so * we have to look at pgndx - 1. */ next_pgno = OADDR_TO_PAGE(DATA_OFF(pagep, (cursorp->pgndx - 1))); if (!hold_pagep) __put_page(hashp, pagep, A_RAW, 0); pagep = __get_page(hashp, next_pgno, A_RAW); if (!pagep) return (-1); /* While there are both keys to compare. */ while ((ksize > 0) && (BIGKEYLEN(pagep))) { if (ksize < KEY_OFF(pagep, 0) || memcmp(BIGKEY(pagep), kkey, BIGKEYLEN(pagep))) { __put_page(hashp, pagep, A_RAW, 0); return (0); } kkey += BIGKEYLEN(pagep); ksize -= BIGKEYLEN(pagep); if (NEXT_PGNO(pagep) != INVALID_PGNO) { next_pgno = NEXT_PGNO(pagep); __put_page(hashp, pagep, A_RAW, 0); pagep = __get_page(hashp, next_pgno, A_RAW); if (!pagep) return (-1); } } __put_page(hashp, pagep, A_RAW, 0); #ifdef DEBUG assert(ksize >= 0); #endif if (ksize != 0) { #ifdef HASH_STATISTICS ++hash_collisions; #endif return (0); } else return (1); } /* * Fill in the key and data for this big pair. */ int32_t __big_keydata(hashp, pagep, key, val, ndx) HTAB *hashp; PAGE16 *pagep; DBT *key, *val; int32_t ndx; { ITEM_INFO ii; PAGE16 *key_pagep; db_pgno_t last_page; key_pagep = __get_page(hashp, OADDR_TO_PAGE(DATA_OFF(pagep, ndx)), A_RAW); if (!key_pagep) return (-1); key->size = collect_key(hashp, key_pagep, 0, &last_page); key->data = hashp->bigkey_buf; __put_page(hashp, key_pagep, A_RAW, 0); if (key->size == -1) return (-1); /* Create an item_info to direct __big_return to the beginning pgno. */ ii.pgno = last_page; return (__big_return(hashp, &ii, val, 1)); } /* * Return the big key on page, ndx. */ int32_t #ifdef __STDC__ __get_bigkey(HTAB *hashp, PAGE16 *pagep, indx_t ndx, DBT *key) #else __get_bigkey(hashp, pagep, ndx, key) HTAB *hashp; PAGE16 *pagep; u_int32_t ndx; DBT *key; #endif { PAGE16 *key_pagep; key_pagep = __get_page(hashp, OADDR_TO_PAGE(DATA_OFF(pagep, ndx)), A_RAW); if (!pagep) return (-1); key->size = collect_key(hashp, key_pagep, 0, NULL); key->data = hashp->bigkey_buf; __put_page(hashp, key_pagep, A_RAW, 0); return (0); } /* * Return the big key and data indicated in item_info. */ int32_t __big_return(hashp, item_info, val, on_bigkey_page) HTAB *hashp; ITEM_INFO *item_info; DBT *val; int32_t on_bigkey_page; { PAGE16 *pagep; db_pgno_t next_pgno; if (!on_bigkey_page) { /* Get first page with big pair on it. */ pagep = __get_page(hashp, OADDR_TO_PAGE(item_info->data_off), A_RAW); if (!pagep) return (-1); } else { pagep = __get_page(hashp, item_info->pgno, A_RAW); if (!pagep) return (-1); } /* Traverse through the bigkey pages until a page with data is found. */ while (!BIGDATALEN(pagep)) { next_pgno = NEXT_PGNO(pagep); __put_page(hashp, pagep, A_RAW, 0); pagep = __get_page(hashp, next_pgno, A_RAW); if (!pagep) return (-1); } val->size = collect_data(hashp, pagep, 0); if (val->size < 1) return (-1); val->data = (void *)hashp->bigdata_buf; __put_page(hashp, pagep, A_RAW, 0); return (0); } /* * Given a page with a big key on it, traverse through the pages counting data * length, and collect all of the data on the way up. Store the key in * hashp->bigkey_buf. last_page indicates to the calling function what the * last page with key on it is; this will help if you later want to retrieve * the data portion. * * Does the work for __get_bigkey. * * Return total length of data; -1 if error. */ static int32_t collect_key(hashp, pagep, len, last_page) HTAB *hashp; PAGE16 *pagep; int32_t len; db_pgno_t *last_page; { PAGE16 *next_pagep; int32_t totlen, retval; db_pgno_t next_pgno; #ifdef DEBUG db_pgno_t save_addr; #endif /* If this is the last page with key. */ if (BIGDATALEN(pagep)) { totlen = len + BIGKEYLEN(pagep); if (hashp->bigkey_buf) free(hashp->bigkey_buf); hashp->bigkey_buf = (u_int8_t *)malloc(totlen); if (!hashp->bigkey_buf) return (-1); memcpy(hashp->bigkey_buf + len, BIGKEY(pagep), BIGKEYLEN(pagep)); if (last_page) *last_page = ADDR(pagep); return (totlen); } /* Key filled up all of last key page, so we've gone 1 too far. */ if (BIGKEYLEN(pagep) == 0) { if (hashp->bigkey_buf) free(hashp->bigkey_buf); hashp->bigkey_buf = (u_int8_t *)malloc(len); return (hashp->bigkey_buf ? len : -1); } totlen = len + BIGKEYLEN(pagep); /* Set pagep to the next page in the chain. */ if (last_page) *last_page = ADDR(pagep); next_pgno = NEXT_PGNO(pagep); next_pagep = __get_page(hashp, next_pgno, A_RAW); if (!next_pagep) return (-1); #ifdef DEBUG save_addr = ADDR(pagep); #endif retval = collect_key(hashp, next_pagep, totlen, last_page); #ifdef DEBUG assert(save_addr == ADDR(pagep)); #endif memcpy(hashp->bigkey_buf + len, BIGKEY(pagep), BIGKEYLEN(pagep)); __put_page(hashp, next_pagep, A_RAW, 0); return (retval); } /* * Given a page with big data on it, recur through the pages counting data * length, and collect all of the data on the way up. Store the data in * hashp->bigdata_buf. * * Does the work for __big_return. * * Return total length of data; -1 if error. */ static int32_t collect_data(hashp, pagep, len) HTAB *hashp; PAGE16 *pagep; int32_t len; { PAGE16 *next_pagep; int32_t totlen, retval; db_pgno_t next_pgno; #ifdef DEBUG db_pgno_t save_addr; #endif /* If there is no next page. */ if (NEXT_PGNO(pagep) == INVALID_PGNO) { if (hashp->bigdata_buf) free(hashp->bigdata_buf); totlen = len + BIGDATALEN(pagep); hashp->bigdata_buf = (u_int8_t *)malloc(totlen); if (!hashp->bigdata_buf) return (-1); memcpy(hashp->bigdata_buf + totlen - BIGDATALEN(pagep), BIGDATA(pagep), BIGDATALEN(pagep)); return (totlen); } totlen = len + BIGDATALEN(pagep); /* Set pagep to the next page in the chain. */ next_pgno = NEXT_PGNO(pagep); next_pagep = __get_page(hashp, next_pgno, A_RAW); if (!next_pagep) return (-1); #ifdef DEBUG save_addr = ADDR(pagep); #endif retval = collect_data(hashp, next_pagep, totlen); #ifdef DEBUG assert(save_addr == ADDR(pagep)); #endif memcpy(hashp->bigdata_buf + totlen - BIGDATALEN(pagep), BIGDATA(pagep), BIGDATALEN(pagep)); __put_page(hashp, next_pagep, A_RAW, 0); return (retval); } /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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 #include "db-int.h" #include "hash.h" #include "page.h" #include "extern.h" #if 0 static u_int32_t hash1 __P((const void *, size_t)); static u_int32_t hash2 __P((const void *, size_t)); static u_int32_t hash3 __P((const void *, size_t)); #endif static u_int32_t hash4 __P((const void *, size_t)); /* Default hash function. */ u_int32_t (*__default_hash) __P((const void *, size_t)) = hash4; /* * Assume that we've already split the bucket to which this key hashes, * calculate that bucket, and check that in fact we did already split it. * * EJB's original hsearch hash. */ #define PRIME1 37 #define PRIME2 1048583 #if 0 static u_int32_t hash1(key, len) const void *key; size_t len; { u_int32_t h; u_int8_t *k; h = 0; k = (u_int8_t *)key; /* Convert string to integer */ while (len--) h = h * PRIME1 ^ (*k++ - ' '); h %= PRIME2; return (h); } /* * Phong Vo's linear congruential hash */ #define dcharhash(h, c) ((h) = 0x63c63cd9*(h) + 0x9c39c33d + (c)) static u_int32_t hash2(key, len) const void *key; size_t len; { u_int32_t h; u_int8_t *e, c, *k; k = (u_int8_t *)key; e = k + len; for (h = 0; k != e;) { c = *k++; if (!c && k > e) break; dcharhash(h, c); } return (h); } /* * This is INCREDIBLY ugly, but fast. We break the string up into 8 byte * units. On the first time through the loop we get the "leftover bytes" * (strlen % 8). On every other iteration, we perform 8 HASHC's so we handle * all 8 bytes. Essentially, this saves us 7 cmp & branch instructions. If * this routine is heavily used enough, it's worth the ugly coding. * * Ozan Yigit's original sdbm hash. */ static u_int32_t hash3(key, len) const void *key; size_t len; { u_int32_t n, loop; u_int8_t *k; #define HASHC n = *k++ + 65599 * n n = 0; k = (u_int8_t *)key; if (len > 0) { loop = (len + 8 - 1) >> 3; switch (len & (8 - 1)) { case 0: do { /* All fall throughs */ HASHC; case 7: HASHC; case 6: HASHC; case 5: HASHC; case 4: HASHC; case 3: HASHC; case 2: HASHC; case 1: HASHC; } while (--loop); } } return (n); } #endif /* Chris Torek's hash function. */ static u_int32_t hash4(const void *key, size_t len) { u_int32_t h, loop; const u_int8_t *k; #define HASH4a h = (h << 5) - h + *k++; #define HASH4b h = (h << 5) + h + *k++; #define HASH4 HASH4b h = 0; k = (const u_int8_t *)key; if (len > 0) { loop = (len + 8 - 1) >> 3; switch (len & (8 - 1)) { case 0: do { /* All fall throughs */ HASH4; /* FALLTHROUGH */ case 7: HASH4; /* FALLTHROUGH */ case 6: HASH4; /* FALLTHROUGH */ case 5: HASH4; /* FALLTHROUGH */ case 4: HASH4; /* FALLTHROUGH */ case 3: HASH4; /* FALLTHROUGH */ case 2: HASH4; /* FALLTHROUGH */ case 1: HASH4; } while (--loop); } } return (h); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)hash_log2.c 8.4 (Berkeley) 11/7/95"; #endif /* LIBC_SCCS and not lint */ #include "db-int.h" #include "hash.h" #include "page.h" #include "extern.h" u_int32_t __kdb2_log2(num) u_int32_t num; { u_int32_t i, limit; limit = 1; for (i = 0; limit < num; limit = limit << 1, i++); return (i); } /* * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ /* * PACKAGE: hashing * * DESCRIPTION: * Page manipulation for hashing package. * * ROUTINES: * * External * __get_page * __add_ovflpage * Internal * overflow_page * open_temp */ #include #ifdef DEBUG #include #endif #include #include #include #include #include #include "db-int.h" #include "hash.h" #include "page.h" #include "extern.h" static int32_t add_bigptr __P((HTAB *, ITEM_INFO *, indx_t)); static u_int32_t *fetch_bitmap __P((HTAB *, int32_t)); static u_int32_t first_free __P((u_int32_t)); #ifdef DEBUG static indx_t next_realkey __P((PAGE16 *, indx_t)); #endif static u_int16_t overflow_page __P((HTAB *)); static void page_init __P((HTAB *, PAGE16 *, db_pgno_t, u_int8_t)); static indx_t prev_realkey __P((PAGE16 *, indx_t)); static void putpair __P((PAGE8 *, const DBT *, const DBT *)); static void swap_page_header_in __P((PAGE16 *)); static void swap_page_header_out __P((PAGE16 *)); #ifdef DEBUG_SLOW static void account_page(HTAB *, db_pgno_t, int); #endif u_int32_t __get_item(hashp, cursorp, key, val, item_info) HTAB *hashp; CURSOR *cursorp; DBT *key, *val; ITEM_INFO *item_info; { db_pgno_t next_pgno; int32_t i; /* Check if we need to get a page. */ if (!cursorp->pagep) { if (cursorp->pgno == INVALID_PGNO) { cursorp->pagep = __get_page(hashp, cursorp->bucket, A_BUCKET); cursorp->pgno = ADDR(cursorp->pagep); cursorp->ndx = 0; cursorp->pgndx = 0; } else cursorp->pagep = __get_page(hashp, cursorp->pgno, A_RAW); if (!cursorp->pagep) { item_info->status = ITEM_ERROR; return (-1); } } if (item_info->seek_size && FREESPACE(cursorp->pagep) > item_info->seek_size) item_info->seek_found_page = cursorp->pgno; if (cursorp->pgndx == NUM_ENT(cursorp->pagep)) { /* Fetch next page. */ if (NEXT_PGNO(cursorp->pagep) == INVALID_PGNO) { item_info->status = ITEM_NO_MORE; return (-1); } next_pgno = NEXT_PGNO(cursorp->pagep); cursorp->pgndx = 0; __put_page(hashp, cursorp->pagep, A_RAW, 0); cursorp->pagep = __get_page(hashp, next_pgno, A_RAW); if (!cursorp->pagep) { item_info->status = ITEM_ERROR; return (-1); } cursorp->pgno = next_pgno; } if (KEY_OFF(cursorp->pagep, cursorp->pgndx) != BIGPAIR) { if ((i = prev_realkey(cursorp->pagep, cursorp->pgndx)) == cursorp->pgndx) key->size = hashp->hdr.bsize - KEY_OFF(cursorp->pagep, cursorp->pgndx); else key->size = DATA_OFF(cursorp->pagep, i) - KEY_OFF(cursorp->pagep, cursorp->pgndx); } /* * All of this information will be set incorrectly for big keys, but * it will be ignored anyway. */ val->size = KEY_OFF(cursorp->pagep, cursorp->pgndx) - DATA_OFF(cursorp->pagep, cursorp->pgndx); key->data = KEY(cursorp->pagep, cursorp->pgndx); val->data = DATA(cursorp->pagep, cursorp->pgndx); item_info->pgno = cursorp->pgno; item_info->bucket = cursorp->bucket; item_info->ndx = cursorp->ndx; item_info->pgndx = cursorp->pgndx; item_info->key_off = KEY_OFF(cursorp->pagep, cursorp->pgndx); item_info->data_off = DATA_OFF(cursorp->pagep, cursorp->pgndx); item_info->status = ITEM_OK; return (0); } u_int32_t __get_item_reset(hashp, cursorp) HTAB *hashp; CURSOR *cursorp; { if (cursorp->pagep) __put_page(hashp, cursorp->pagep, A_RAW, 0); cursorp->pagep = NULL; cursorp->bucket = -1; cursorp->ndx = 0; cursorp->pgndx = 0; cursorp->pgno = INVALID_PGNO; return (0); } u_int32_t __get_item_done(hashp, cursorp) HTAB *hashp; CURSOR *cursorp; { if (cursorp->pagep) __put_page(hashp, cursorp->pagep, A_RAW, 0); cursorp->pagep = NULL; /* * We don't throw out the page number since we might want to * continue getting on this page. */ return (0); } u_int32_t __get_item_first(hashp, cursorp, key, val, item_info) HTAB *hashp; CURSOR *cursorp; DBT *key, *val; ITEM_INFO *item_info; { __get_item_reset(hashp, cursorp); cursorp->bucket = 0; return (__get_item_next(hashp, cursorp, key, val, item_info)); } /* * Returns a pointer to key/data pair on a page. In the case of bigkeys, * just returns the page number and index of the bigkey pointer pair. */ u_int32_t __get_item_next(hashp, cursorp, key, val, item_info) HTAB *hashp; CURSOR *cursorp; DBT *key, *val; ITEM_INFO *item_info; { int status; status = __get_item(hashp, cursorp, key, val, item_info); cursorp->ndx++; cursorp->pgndx++; return (status); } /* * Put a non-big pair on a page. */ static void putpair(p, key, val) PAGE8 *p; const DBT *key, *val; { u_int16_t *pagep, n, off; pagep = (PAGE16 *)p; /* Items on the page are 0-indexed. */ n = NUM_ENT(pagep); off = OFFSET(pagep) - key->size + 1; memmove(p + off, key->data, key->size); KEY_OFF(pagep, n) = off; off -= val->size; memmove(p + off, val->data, val->size); DATA_OFF(pagep, n) = off; /* Adjust page info. */ NUM_ENT(pagep) = n + 1; OFFSET(pagep) = off - 1; } /* * Returns the index of the next non-bigkey pair after n on the page. * Returns -1 if there are no more non-big things on the page. */ #ifdef DEBUG static indx_t next_realkey(PAGE16 * pagep, indx_t n) { indx_t i; for (i = n + 1; i < NUM_ENT(pagep); i++) if (KEY_OFF(pagep, i) != BIGPAIR) return (i); return (-1); } #endif /* * Returns the index of the previous non-bigkey pair after n on the page. * Returns n if there are no previous non-big things on the page. */ static indx_t #ifdef __STDC__ prev_realkey(PAGE16 * pagep, indx_t n) #else prev_realkey(pagep, n) PAGE16 *pagep; u_int32_t n; #endif { int32_t i; /* Need a signed value to do the compare properly. */ for (i = n - 1; i > -1; i--) if (KEY_OFF(pagep, i) != BIGPAIR) return (i); return (n); } /* * Returns: * 0 OK * -1 error */ extern int32_t __delpair(hashp, cursorp, item_info) HTAB *hashp; CURSOR *cursorp; ITEM_INFO *item_info; { PAGE16 *pagep; indx_t ndx; short check_ndx; int16_t delta, len; int32_t n; u_int8_t *src, *dest; ndx = cursorp->pgndx; if (!cursorp->pagep) { pagep = __get_page(hashp, cursorp->pgno, A_RAW); if (!pagep) return (-1); /* * KLUGE: pgndx has gone one too far, because cursor points * to the _next_ item. Use pgndx - 1. */ --ndx; } else pagep = cursorp->pagep; #ifdef DEBUG assert(ADDR(pagep) == cursorp->pgno); #endif if (KEY_OFF(pagep, ndx) == BIGPAIR) { delta = 0; __big_delete(hashp, pagep, ndx); } else { /* * Compute "delta", the amount we have to shift all of the * offsets. To find the delta, we need to make sure that * we aren't looking at the DATA_OFF of a big/keydata pair. */ for (check_ndx = (short)(ndx - 1); check_ndx >= 0 && KEY_OFF(pagep, check_ndx) == BIGPAIR; check_ndx--); if (check_ndx < 0) delta = hashp->hdr.bsize - DATA_OFF(pagep, ndx); else delta = DATA_OFF(pagep, check_ndx) - DATA_OFF(pagep, ndx); /* * The hard case: we want to remove something other than * the last item on the page. We need to shift data and * offsets down. */ if (ndx != NUM_ENT(pagep) - 1) { /* * Move the data: src is the address of the last data * item on the page. */ src = (u_int8_t *)pagep + OFFSET(pagep) + 1; /* * Length is the distance between where to start * deleting and end of the data on the page. */ len = DATA_OFF(pagep, ndx) - (OFFSET(pagep) + 1); /* * Dest is the location of the to-be-deleted item * occupied - length. */ if (check_ndx < 0) dest = (u_int8_t *)pagep + hashp->hdr.bsize - len; else dest = (u_int8_t *)pagep + DATA_OFF(pagep, (check_ndx)) - len; memmove(dest, src, len); } } /* Adjust the offsets. */ for (n = ndx; n < NUM_ENT(pagep) - 1; n++) if (KEY_OFF(pagep, (n + 1)) != BIGPAIR) { #ifdef DEBUG assert(next_realkey(pagep, n) != -1); #endif KEY_OFF(pagep, n) = KEY_OFF(pagep, (n + 1)) + delta; DATA_OFF(pagep, n) = DATA_OFF(pagep, (n + 1)) + delta; } else { KEY_OFF(pagep, n) = KEY_OFF(pagep, (n + 1)); DATA_OFF(pagep, n) = DATA_OFF(pagep, (n + 1)); } /* Adjust page metadata. */ OFFSET(pagep) = OFFSET(pagep) + delta; NUM_ENT(pagep) = NUM_ENT(pagep) - 1; --hashp->hdr.nkeys; /* Is this page now an empty overflow page? If so, free it. */ if (TYPE(pagep) == HASH_OVFLPAGE && NUM_ENT(pagep) == 0) { PAGE16 *empty_page; db_pgno_t to_find, next_pgno, link_page; /* * We need to go back to the first page in the chain and * look for this page so that we can update the previous * page's NEXT_PGNO field. */ to_find = ADDR(pagep); empty_page = pagep; link_page = NEXT_PGNO(empty_page); pagep = __get_page(hashp, item_info->bucket, A_BUCKET); if (!pagep) return (-1); while (NEXT_PGNO(pagep) != to_find) { next_pgno = NEXT_PGNO(pagep); #ifdef DEBUG assert(next_pgno != INVALID_PGNO); #endif __put_page(hashp, pagep, A_RAW, 0); pagep = __get_page(hashp, next_pgno, A_RAW); if (!pagep) return (-1); } /* * At this point, pagep should point to the page before the * page to be deleted. */ NEXT_PGNO(pagep) = link_page; if (item_info->pgno == to_find) { item_info->pgno = ADDR(pagep); item_info->pgndx = NUM_ENT(pagep); item_info->seek_found_page = ADDR(pagep); } __delete_page(hashp, empty_page, A_OVFL); } __put_page(hashp, pagep, A_RAW, 1); return (0); } extern int32_t __split_page(hashp, obucket, nbucket) HTAB *hashp; u_int32_t obucket, nbucket; { DBT key, val; ITEM_INFO old_ii, new_ii; PAGE16 *old_pagep, *temp_pagep; db_pgno_t next_pgno; int32_t off; u_int16_t n; int8_t base_page; off = hashp->hdr.bsize; old_pagep = __get_page(hashp, obucket, A_BUCKET); base_page = 1; temp_pagep = hashp->split_buf; memcpy(temp_pagep, old_pagep, hashp->hdr.bsize); page_init(hashp, old_pagep, ADDR(old_pagep), HASH_PAGE); __put_page(hashp, old_pagep, A_RAW, 1); old_ii.pgno = BUCKET_TO_PAGE(obucket); new_ii.pgno = BUCKET_TO_PAGE(nbucket); old_ii.bucket = obucket; new_ii.bucket = nbucket; old_ii.seek_found_page = new_ii.seek_found_page = 0; while (temp_pagep != 0) { off = hashp->hdr.bsize; for (n = 0; n < NUM_ENT(temp_pagep); n++) { if (KEY_OFF(temp_pagep, n) == BIGPAIR) { __get_bigkey(hashp, temp_pagep, n, &key); if (__call_hash(hashp, key.data, key.size) == obucket) add_bigptr(hashp, &old_ii, DATA_OFF(temp_pagep, n)); else add_bigptr(hashp, &new_ii, DATA_OFF(temp_pagep, n)); } else { key.size = off - KEY_OFF(temp_pagep, n); key.data = KEY(temp_pagep, n); off = KEY_OFF(temp_pagep, n); val.size = off - DATA_OFF(temp_pagep, n); val.data = DATA(temp_pagep, n); if (__call_hash(hashp, key.data, key.size) == obucket) __addel(hashp, &old_ii, &key, &val, NO_EXPAND, 1); else __addel(hashp, &new_ii, &key, &val, NO_EXPAND, 1); off = DATA_OFF(temp_pagep, n); } } next_pgno = NEXT_PGNO(temp_pagep); /* Clear temp_page; if it's an overflow page, free it. */ if (!base_page) __delete_page(hashp, temp_pagep, A_OVFL); else base_page = 0; if (next_pgno != INVALID_PGNO) temp_pagep = __get_page(hashp, next_pgno, A_RAW); else break; } return (0); } /* * Add the given pair to the page. * * * Returns: * 0 ==> OK * -1 ==> failure */ extern int32_t #ifdef __STDC__ __addel(HTAB *hashp, ITEM_INFO *item_info, const DBT *key, const DBT *val, u_int32_t num_items, const u_int8_t expanding) #else __addel(hashp, item_info, key, val, num_items, expanding) HTAB *hashp; ITEM_INFO *item_info; const DBT *key, *val; u_int32_t num_items; const u_int32_t expanding; #endif { PAGE16 *pagep; int32_t do_expand; db_pgno_t next_pgno; do_expand = 0; pagep = __get_page(hashp, item_info->seek_found_page != 0 ? item_info->seek_found_page : item_info->pgno, A_RAW); if (!pagep) return (-1); /* Advance to first page in chain with room for item. */ while (NUM_ENT(pagep) && NEXT_PGNO(pagep) != INVALID_PGNO) { /* * This may not be the end of the chain, but the pair may fit * anyway. */ if (ISBIG(PAIRSIZE(key, val), hashp) && BIGPAIRFITS(pagep)) break; if (PAIRFITS(pagep, key, val)) break; next_pgno = NEXT_PGNO(pagep); __put_page(hashp, pagep, A_RAW, 0); pagep = (PAGE16 *)__get_page(hashp, next_pgno, A_RAW); if (!pagep) return (-1); } if ((ISBIG(PAIRSIZE(key, val), hashp) && !BIGPAIRFITS(pagep)) || (!ISBIG(PAIRSIZE(key, val), hashp) && !PAIRFITS(pagep, key, val))) { do_expand = 1; pagep = __add_ovflpage(hashp, pagep); if (!pagep) return (-1); if ((ISBIG(PAIRSIZE(key, val), hashp) && !BIGPAIRFITS(pagep)) || (!ISBIG(PAIRSIZE(key, val), hashp) && !PAIRFITS(pagep, key, val))) { __put_page(hashp, pagep, A_RAW, 0); return (-1); } } /* At this point, we know the page fits, so we just add it */ if (ISBIG(PAIRSIZE(key, val), hashp)) { if (__big_insert(hashp, pagep, key, val)) return (-1); } else { putpair((PAGE8 *)pagep, key, val); } /* * For splits, we are going to update item_info's page number * field, so that we can easily return to the same page the * next time we come in here. For other operations, this shouldn't * matter, since adds are the last thing that happens before we * return to the user program. */ item_info->pgno = ADDR(pagep); if (!expanding) hashp->hdr.nkeys++; /* Kludge: if this is a big page, then it's already been put. */ if (!ISBIG(PAIRSIZE(key, val), hashp)) __put_page(hashp, pagep, A_RAW, 1); if (expanding) item_info->caused_expand = 0; else switch (num_items) { case NO_EXPAND: item_info->caused_expand = 0; break; case UNKNOWN: item_info->caused_expand |= (hashp->hdr.nkeys / hashp->hdr.max_bucket) > hashp->hdr.ffactor || item_info->pgndx > hashp->hdr.ffactor; break; default: item_info->caused_expand = num_items > hashp->hdr.ffactor ? 1 : do_expand; break; } return (0); } /* * Special __addel used in big splitting; this one just puts the pointer * to an already-allocated big page in the appropriate bucket. */ static int32_t #ifdef __STDC__ add_bigptr(HTAB * hashp, ITEM_INFO * item_info, indx_t big_pgno) #else add_bigptr(hashp, item_info, big_pgno) HTAB *hashp; ITEM_INFO *item_info; u_int32_t big_pgno; #endif { PAGE16 *pagep; db_pgno_t next_pgno; pagep = __get_page(hashp, item_info->bucket, A_BUCKET); if (!pagep) return (-1); /* * Note: in __addel(), we used item_info->pgno for the beginning of * our search for space. Now, we use item_info->bucket, since we * know that the space required by a big pair on the base page is * quite small, and we may very well find that space early in the * chain. */ /* Find first page in chain that has space for a big pair. */ while (NUM_ENT(pagep) && (NEXT_PGNO(pagep) != INVALID_PGNO)) { if (BIGPAIRFITS(pagep)) break; next_pgno = NEXT_PGNO(pagep); __put_page(hashp, pagep, A_RAW, 0); pagep = __get_page(hashp, next_pgno, A_RAW); if (!pagep) return (-1); } if (!BIGPAIRFITS(pagep)) { pagep = __add_ovflpage(hashp, pagep); if (!pagep) return (-1); #ifdef DEBUG assert(BIGPAIRFITS(pagep)); #endif } KEY_OFF(pagep, NUM_ENT(pagep)) = BIGPAIR; DATA_OFF(pagep, NUM_ENT(pagep)) = big_pgno; NUM_ENT(pagep) = NUM_ENT(pagep) + 1; __put_page(hashp, pagep, A_RAW, 1); return (0); } /* * * Returns: * pointer on success * NULL on error */ extern PAGE16 * __add_ovflpage(hashp, pagep) HTAB *hashp; PAGE16 *pagep; { PAGE16 *new_pagep; u_int16_t ovfl_num; /* Check if we are dynamically determining the fill factor. */ if (hashp->hdr.ffactor == DEF_FFACTOR) { hashp->hdr.ffactor = NUM_ENT(pagep) >> 1; if (hashp->hdr.ffactor < MIN_FFACTOR) hashp->hdr.ffactor = MIN_FFACTOR; } ovfl_num = overflow_page(hashp); if (!ovfl_num) return (NULL); if (__new_page(hashp, (u_int32_t)ovfl_num, A_OVFL) != 0) return (NULL); if (!ovfl_num || !(new_pagep = __get_page(hashp, ovfl_num, A_OVFL))) return (NULL); NEXT_PGNO(pagep) = (db_pgno_t)OADDR_TO_PAGE(ovfl_num); TYPE(new_pagep) = HASH_OVFLPAGE; __put_page(hashp, pagep, A_RAW, 1); #ifdef HASH_STATISTICS hash_overflows++; #endif return (new_pagep); } /* * * Returns: * pointer on success * NULL on error */ extern PAGE16 * #ifdef __STDC__ __add_bigpage(HTAB * hashp, PAGE16 * pagep, indx_t ndx, const u_int8_t is_basepage) #else __add_bigpage(hashp, pagep, ndx, is_basepage) HTAB *hashp; PAGE16 *pagep; u_int32_t ndx; const u_int32_t is_basepage; #endif { PAGE16 *new_pagep; u_int16_t ovfl_num; ovfl_num = overflow_page(hashp); if (!ovfl_num) return (NULL); if (__new_page(hashp, (u_int32_t)ovfl_num, A_OVFL) != 0) return (NULL); if (!ovfl_num || !(new_pagep = __get_page(hashp, ovfl_num, A_OVFL))) return (NULL); if (is_basepage) { KEY_OFF(pagep, ndx) = BIGPAIR; DATA_OFF(pagep, ndx) = (indx_t)ovfl_num; } else NEXT_PGNO(pagep) = ADDR(new_pagep); __put_page(hashp, pagep, A_RAW, 1); TYPE(new_pagep) = HASH_BIGPAGE; #ifdef HASH_STATISTICS hash_bigpages++; #endif return (new_pagep); } static void #ifdef __STDC__ page_init(HTAB * hashp, PAGE16 * pagep, db_pgno_t pgno, u_int8_t type) #else page_init(hashp, pagep, pgno, type) HTAB *hashp; PAGE16 *pagep; db_pgno_t pgno; u_int32_t type; #endif { NUM_ENT(pagep) = 0; PREV_PGNO(pagep) = NEXT_PGNO(pagep) = INVALID_PGNO; TYPE(pagep) = type; OFFSET(pagep) = hashp->hdr.bsize - 1; /* * Note: since in the current version ADDR(pagep) == PREV_PGNO(pagep), * make sure that ADDR(pagep) is set after resetting PREV_PGNO(pagep). * We reset PREV_PGNO(pagep) just in case the macros are changed. */ ADDR(pagep) = pgno; return; } int32_t __new_page(hashp, addr, addr_type) HTAB *hashp; u_int32_t addr; int32_t addr_type; { db_pgno_t paddr; PAGE16 *pagep; switch (addr_type) { /* Convert page number. */ case A_BUCKET: paddr = BUCKET_TO_PAGE(addr); break; case A_OVFL: case A_BITMAP: paddr = OADDR_TO_PAGE(addr); break; default: paddr = addr; break; } pagep = mpool_new(hashp->mp, &paddr, MPOOL_PAGE_REQUEST); if (!pagep) return (-1); #if DEBUG_SLOW account_page(hashp, paddr, 1); #endif if (addr_type != A_BITMAP) page_init(hashp, pagep, paddr, HASH_PAGE); __put_page(hashp, pagep, addr_type, 1); return (0); } int32_t __delete_page(hashp, pagep, page_type) HTAB *hashp; PAGE16 *pagep; int32_t page_type; { if (page_type == A_OVFL) __free_ovflpage(hashp, pagep); return (mpool_delete(hashp->mp, pagep)); } static u_int8_t is_bitmap_pgno(hashp, pgno) HTAB *hashp; db_pgno_t pgno; { int32_t i; for (i = 0; i < hashp->nmaps; i++) if (OADDR_TO_PAGE(hashp->hdr.bitmaps[i]) == pgno) return (1); return (0); } void __pgin_routine(pg_cookie, pgno, page) void *pg_cookie; db_pgno_t pgno; void *page; { HTAB *hashp; PAGE16 *pagep; int32_t max, i; pagep = (PAGE16 *)page; hashp = (HTAB *)pg_cookie; /* * There are the following cases for swapping: * 0) New page that may be unitialized. * 1) Bucket page or overflow page. Either swap * the header or initialize the page. * 2) Bitmap page. Swap the whole page! * 3) Header pages. Not handled here; these are written directly * to the file. */ if (NUM_ENT(pagep) == 0 && NEXT_PGNO(pagep) == 0 && !is_bitmap_pgno(hashp, pgno)) { /* XXX check for !0 LSN */ page_init(hashp, pagep, pgno, HASH_PAGE); return; } if (hashp->hdr.lorder == DB_BYTE_ORDER) return; if (is_bitmap_pgno(hashp, pgno)) { max = hashp->hdr.bsize >> 2; /* divide by 4 bytes */ for (i = 0; i < max; i++) M_32_SWAP(((int32_t *)pagep)[i]); } else swap_page_header_in(pagep); } void __pgout_routine(pg_cookie, pgno, page) void *pg_cookie; db_pgno_t pgno; void *page; { HTAB *hashp; PAGE16 *pagep; int32_t i, max; pagep = (PAGE16 *)page; hashp = (HTAB *)pg_cookie; /* * There are the following cases for swapping: * 1) Bucket page or overflow page. Just swap the header. * 2) Bitmap page. Swap the whole page! * 3) Header pages. Not handled here; these are written directly * to the file. */ if (hashp->hdr.lorder == DB_BYTE_ORDER) return; if (is_bitmap_pgno(hashp, pgno)) { max = hashp->hdr.bsize >> 2; /* divide by 4 bytes */ for (i = 0; i < max; i++) M_32_SWAP(((int32_t *)pagep)[i]); } else swap_page_header_out(pagep); } /* * * Returns: * 0 ==> OK * -1 ==>failure */ extern int32_t __put_page(hashp, pagep, addr_type, is_dirty) HTAB *hashp; PAGE16 *pagep; int32_t addr_type, is_dirty; { #if DEBUG_SLOW account_page(hashp, ((BKT *)((char *)pagep - sizeof(BKT)))->pgno, -1); #endif return (mpool_put(hashp->mp, pagep, (is_dirty ? MPOOL_DIRTY : 0))); } /* * Returns: * 0 indicates SUCCESS * -1 indicates FAILURE */ extern PAGE16 * __get_page(hashp, addr, addr_type) HTAB *hashp; u_int32_t addr; int32_t addr_type; { PAGE16 *pagep; db_pgno_t paddr; switch (addr_type) { /* Convert page number. */ case A_BUCKET: paddr = BUCKET_TO_PAGE(addr); break; case A_OVFL: case A_BITMAP: paddr = OADDR_TO_PAGE(addr); break; default: paddr = addr; break; } pagep = (PAGE16 *)mpool_get(hashp->mp, paddr, 0); #if DEBUG_SLOW account_page(hashp, paddr, 1); #endif #ifdef DEBUG assert(ADDR(pagep) == paddr || ADDR(pagep) == 0 || addr_type == A_BITMAP || addr_type == A_HEADER); #endif return (pagep); } static void swap_page_header_in(pagep) PAGE16 *pagep; { u_int32_t i; /* can leave type and filler alone, since they're 1-byte quantities */ M_32_SWAP(PREV_PGNO(pagep)); M_32_SWAP(NEXT_PGNO(pagep)); M_16_SWAP(NUM_ENT(pagep)); M_16_SWAP(OFFSET(pagep)); for (i = 0; i < NUM_ENT(pagep); i++) { M_16_SWAP(KEY_OFF(pagep, i)); M_16_SWAP(DATA_OFF(pagep, i)); } } static void swap_page_header_out(pagep) PAGE16 *pagep; { u_int32_t i; for (i = 0; i < NUM_ENT(pagep); i++) { M_16_SWAP(KEY_OFF(pagep, i)); M_16_SWAP(DATA_OFF(pagep, i)) } /* can leave type and filler alone, since they're 1-byte quantities */ M_32_SWAP(PREV_PGNO(pagep)); M_32_SWAP(NEXT_PGNO(pagep)); M_16_SWAP(NUM_ENT(pagep)); M_16_SWAP(OFFSET(pagep)); } #define BYTE_MASK ((1 << INT32_T_BYTE_SHIFT) -1) /* * Initialize a new bitmap page. Bitmap pages are left in memory * once they are read in. */ extern int32_t __ibitmap(hashp, pnum, nbits, ndx) HTAB *hashp; int32_t pnum, nbits, ndx; { u_int32_t *ip; int32_t clearbytes, clearints; /* make a new bitmap page */ if (__new_page(hashp, pnum, A_BITMAP) != 0) return (1); if (!(ip = (u_int32_t *)__get_page(hashp, pnum, A_BITMAP))) return (1); hashp->nmaps++; clearints = ((nbits - 1) >> INT32_T_BYTE_SHIFT) + 1; clearbytes = clearints << INT32_T_TO_BYTE; (void)memset((int8_t *)ip, 0, clearbytes); (void)memset((int8_t *)ip + clearbytes, 0xFF, hashp->hdr.bsize - clearbytes); ip[clearints - 1] = ALL_SET << (nbits & BYTE_MASK); SETBIT(ip, 0); hashp->hdr.bitmaps[ndx] = (u_int16_t)pnum; hashp->mapp[ndx] = ip; return (0); } static u_int32_t first_free(map) u_int32_t map; { u_int32_t i, mask; for (mask = 0x1, i = 0; i < BITS_PER_MAP; i++) { if (!(mask & map)) return (i); mask = mask << 1; } return (i); } /* * returns 0 on error */ static u_int16_t overflow_page(hashp) HTAB *hashp; { u_int32_t *freep; int32_t bit, first_page, free_bit, free_page, i, in_use_bits, j; int32_t max_free, offset, splitnum; u_int16_t addr; #ifdef DEBUG2 int32_t tmp1, tmp2; #endif splitnum = hashp->hdr.ovfl_point; max_free = hashp->hdr.spares[splitnum]; free_page = (max_free - 1) >> (hashp->hdr.bshift + BYTE_SHIFT); free_bit = (max_free - 1) & ((hashp->hdr.bsize << BYTE_SHIFT) - 1); /* * Look through all the free maps to find the first free block. * The compiler under -Wall will complain that freep may be used * before being set, however, this loop will ALWAYS get executed * at least once, so freep is guaranteed to be set. */ first_page = hashp->hdr.last_freed >> (hashp->hdr.bshift + BYTE_SHIFT); for (i = first_page; i <= free_page; i++) { if (!(freep = fetch_bitmap(hashp, i))) return (0); if (i == free_page) in_use_bits = free_bit; else in_use_bits = (hashp->hdr.bsize << BYTE_SHIFT) - 1; if (i == first_page) { bit = hashp->hdr.last_freed & ((hashp->hdr.bsize << BYTE_SHIFT) - 1); j = bit / BITS_PER_MAP; bit = bit & ~(BITS_PER_MAP - 1); } else { bit = 0; j = 0; } for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP) if (freep[j] != ALL_SET) goto found; } /* No Free Page Found */ hashp->hdr.last_freed = hashp->hdr.spares[splitnum]; hashp->hdr.spares[splitnum]++; offset = hashp->hdr.spares[splitnum] - (splitnum ? hashp->hdr.spares[splitnum - 1] : 0); #define OVMSG "HASH: Out of overflow pages. Increase page size\n" if (offset > SPLITMASK) { if (++splitnum >= NCACHED) { (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); return (0); } hashp->hdr.ovfl_point = splitnum; hashp->hdr.spares[splitnum] = hashp->hdr.spares[splitnum - 1]; hashp->hdr.spares[splitnum - 1]--; offset = 1; } /* Check if we need to allocate a new bitmap page. */ if (free_bit == (hashp->hdr.bsize << BYTE_SHIFT) - 1) { free_page++; if (free_page >= NCACHED) { (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); return (0); } /* * This is tricky. The 1 indicates that you want the new page * allocated with 1 clear bit. Actually, you are going to * allocate 2 pages from this map. The first is going to be * the map page, the second is the overflow page we were * looking for. The __ibitmap routine automatically, sets * the first bit of itself to indicate that the bitmap itself * is in use. We would explicitly set the second bit, but * don't have to if we tell __ibitmap not to leave it clear * in the first place. */ if (__ibitmap(hashp, (int32_t)OADDR_OF(splitnum, offset), 1, free_page)) return (0); hashp->hdr.spares[splitnum]++; #ifdef DEBUG2 free_bit = 2; #endif offset++; if (offset > SPLITMASK) { if (++splitnum >= NCACHED) { (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); return (0); } hashp->hdr.ovfl_point = splitnum; hashp->hdr.spares[splitnum] = hashp->hdr.spares[splitnum - 1]; hashp->hdr.spares[splitnum - 1]--; offset = 0; } } else { /* * Free_bit addresses the last used bit. Bump it to address * the first available bit. */ free_bit++; SETBIT(freep, free_bit); } /* Calculate address of the new overflow page */ addr = OADDR_OF(splitnum, offset); #ifdef DEBUG2 (void)fprintf(stderr, dgettext(TEXT_DOMAIN, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n"), addr, free_bit, free_page); #endif if (OADDR_TO_PAGE(addr) > MAX_PAGES(hashp)) { (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); return (0); } return (addr); found: bit = bit + first_free(freep[j]); SETBIT(freep, bit); #ifdef DEBUG2 tmp1 = bit; tmp2 = i; #endif /* * Bits are addressed starting with 0, but overflow pages are addressed * beginning at 1. Bit is a bit address number, so we need to increment * it to convert it to a page number. */ bit = 1 + bit + (i * (hashp->hdr.bsize << BYTE_SHIFT)); if (bit >= hashp->hdr.last_freed) hashp->hdr.last_freed = bit - 1; /* Calculate the split number for this page */ for (i = 0; i < splitnum && (bit > hashp->hdr.spares[i]); i++); offset = (i ? bit - hashp->hdr.spares[i - 1] : bit); if (offset >= SPLITMASK) return (0); /* Out of overflow pages */ addr = OADDR_OF(i, offset); #ifdef DEBUG2 (void)fprintf(stderr, dgettext(TEXT_DOMAIN, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n"), addr, tmp1, tmp2); #endif if (OADDR_TO_PAGE(addr) > MAX_PAGES(hashp)) { (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); return (0); } /* Allocate and return the overflow page */ return (addr); } #ifdef DEBUG int bucket_to_page(hashp, n) HTAB *hashp; int n; { int ret_val; ret_val = n + hashp->hdr.hdrpages; if (n != 0) ret_val += hashp->hdr.spares[__log2(n + 1) - 1]; return (ret_val); } int32_t oaddr_to_page(hashp, n) HTAB *hashp; int n; { int ret_val, temp; temp = (1 << SPLITNUM(n)) - 1; ret_val = bucket_to_page(hashp, temp); ret_val += (n & SPLITMASK); return (ret_val); } #endif /* DEBUG */ static indx_t page_to_oaddr(hashp, pgno) HTAB *hashp; db_pgno_t pgno; { int32_t sp, ret_val; /* * To convert page number to overflow address: * * 1. Find a starting split point -- use 0 since there are only * 32 split points. * 2. Find the split point s.t. 2^sp + hdr.spares[sp] < pgno and * 2^(sp+1) = hdr.spares[sp+1] > pgno. The overflow address will * be located at sp. * 3. return... */ pgno -= hashp->hdr.hdrpages; for (sp = 0; sp < NCACHED; sp++) if (POW2(sp) + hashp->hdr.spares[sp] < pgno && (POW2(sp + 1) + hashp->hdr.spares[sp + 1]) > pgno) break; ret_val = OADDR_OF(sp + 1, pgno - ((POW2(sp + 1) - 1) + hashp->hdr.spares[sp])); #ifdef DEBUG assert(OADDR_TO_PAGE(ret_val) == (pgno + hashp->hdr.hdrpages)); #endif return (ret_val); } /* * Mark this overflow page as free. */ extern void __free_ovflpage(hashp, pagep) HTAB *hashp; PAGE16 *pagep; { u_int32_t *freep; int32_t bit_address, free_page, free_bit; u_int16_t addr, ndx; addr = page_to_oaddr(hashp, ADDR(pagep)); #ifdef DEBUG2 (void)fprintf(stderr, dgettext(TEXT_DOMAIN, "Freeing %d\n"), addr); #endif ndx = ((u_int16_t)addr) >> SPLITSHIFT; bit_address = (ndx ? hashp->hdr.spares[ndx - 1] : 0) + (addr & SPLITMASK) - 1; if (bit_address < hashp->hdr.last_freed) hashp->hdr.last_freed = bit_address; free_page = (bit_address >> (hashp->hdr.bshift + BYTE_SHIFT)); free_bit = bit_address & ((hashp->hdr.bsize << BYTE_SHIFT) - 1); freep = fetch_bitmap(hashp, free_page); #ifdef DEBUG /* * This had better never happen. It means we tried to read a bitmap * that has already had overflow pages allocated off it, and we * failed to read it from the file. */ if (!freep) assert(0); #endif CLRBIT(freep, free_bit); #ifdef DEBUG2 (void)fprintf(stderr, dgettext(TEXT_DOMAIN, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n"), obufp->addr, free_bit, free_page); #endif } static u_int32_t * fetch_bitmap(hashp, ndx) HTAB *hashp; int32_t ndx; { if (ndx >= hashp->nmaps) return (NULL); if (!hashp->mapp[ndx]) hashp->mapp[ndx] = (u_int32_t *)__get_page(hashp, hashp->hdr.bitmaps[ndx], A_BITMAP); return (hashp->mapp[ndx]); } #ifdef DEBUG_SLOW static void account_page(hashp, pgno, inout) HTAB *hashp; db_pgno_t pgno; int inout; { static struct { db_pgno_t pgno; int times; } list[100]; static int last; int i, j; if (inout == -1) /* XXX: Kluge */ inout = 0; /* Find page in list. */ for (i = 0; i < last; i++) if (list[i].pgno == pgno) break; /* Not found. */ if (i == last) { list[last].times = inout; list[last].pgno = pgno; last++; } list[i].times = inout; if (list[i].times == 0) { for (j = i; j < last; j++) list[j] = list[j + 1]; last--; } for (i = 0; i < last; i++, list[i].times++) if (list[i].times > 20 && !is_bitmap_pgno(hashp, list[i].pgno)) (void)fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: pg %d has been out for %d times\n"), list[i].pgno, list[i].times); } #endif /* DEBUG_SLOW */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)hsearch.c 8.5 (Berkeley) 9/21/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include "db-int.h" #include "search.h" static DB *dbp = NULL; static ENTRY retval; extern int hcreate(nel) u_int nel; { HASHINFO info; info.nelem = nel; info.bsize = 256; info.ffactor = 8; info.cachesize = 0; info.hash = NULL; info.lorder = 0; dbp = (DB *)__hash_open(NULL, O_CREAT | O_RDWR | O_BINARY, 0600, &info, 0); return (dbp != NULL); } extern ENTRY * hsearch(item, action) ENTRY item; ACTION action; { DBT key, val; int status; if (!dbp) return (NULL); key.data = (u_char *)item.key; key.size = strlen(item.key) + 1; if (action == ENTER) { val.data = (u_char *)item.data; val.size = strlen(item.data) + 1; status = (dbp->put)(dbp, &key, &val, R_NOOVERWRITE); if (status) return (NULL); } else { /* FIND */ status = (dbp->get)(dbp, &key, &val, 0); if (status) return (NULL); else item.data = (char *)val.data; } retval.key = item.key; retval.data = item.data; return (&retval); } extern void hdestroy() { if (dbp) { (void)(dbp->close)(dbp); dbp = NULL; } } /* * Copyright (c) 1997-2000 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _KRB5_DB2_PAGE_H #define _KRB5_DB2_PAGE_H #ifdef __cplusplus extern "C" { #endif /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)page.h 8.4 (Berkeley) 11/7/95 */ #define HI_MASK 0xFFFF0000 #define LO_MASK (~HI_MASK) #define HI(N) ((u_int16_t)(((N) & HI_MASK) >> 16)) #define LO(N) ((u_int16_t)((N) & LO_MASK)) /* Constants for big key page overhead information. */ #define NUMSHORTS 0 #define KEYLEN 1 #define DATALEN 2 #define NEXTPAGE 3 /* * Hash pages store meta-data beginning at the top of the page (offset 0) * and key/data values beginning at the bottom of the page (offset pagesize). * Fields are always accessed via macros so that we can change the page * format without too much pain. The only changes that will require massive * code changes are if we no longer store key/data offsets next to each * other (since we use that fact to compute key lengths). In the accessor * macros below, P means a pointer to the page, I means an index of the * particular entry being accessed. * * Hash base page format * BYTE ITEM NBYTES TYPE ACCESSOR MACRO * ---- ------------------ ------ -------- -------------- * 0 previous page number 4 db_pgno_t PREV_PGNO(P) * 4 next page number 4 db_pgno_t NEXT_PGNO(P) * 8 # pairs on page 2 indx_t NUM_ENT(P) * 10 page type 1 u_int8_t TYPE(P) * 11 padding 1 u_int8_t none * 12 highest free byte 2 indx_t OFFSET(P) * 14 key offset 0 2 indx_t KEY_OFF(P, I) * 16 data offset 0 2 indx_t DATA_OFF(P, I) * 18 key offset 1 2 indx_t KEY_OFF(P, I) * 20 data offset 1 2 indx_t DATA_OFF(P, I) * ...etc... */ /* Indices (in bytes) of the beginning of each of these entries */ #define I_PREV_PGNO 0 #define I_NEXT_PGNO 4 #define I_ENTRIES 8 #define I_TYPE 10 #define I_HF_OFFSET 12 /* Overhead is everything prior to the first key/data pair. */ #define PAGE_OVERHEAD (I_HF_OFFSET + sizeof(indx_t)) /* To allocate a pair, we need room for one key offset and one data offset. */ #define PAIR_OVERHEAD ((sizeof(indx_t) << 1)) /* Use this macro to extract a value of type T from page P at offset O. */ #define REFERENCE(P, T, O) (((T *)((u_int8_t *)(P) + O))[0]) /* * Use these macros to access fields on a page; P is a PAGE16 *. */ #define NUM_ENT(P) (REFERENCE((P), indx_t, I_ENTRIES)) #define PREV_PGNO(P) (REFERENCE((P), db_pgno_t, I_PREV_PGNO)) #define NEXT_PGNO(P) (REFERENCE((P), db_pgno_t, I_NEXT_PGNO)) #define TYPE(P) (REFERENCE((P), u_int8_t, I_TYPE)) #define OFFSET(P) (REFERENCE((P), indx_t, I_HF_OFFSET)) /* * We need to store a page's own address on each page (unlike the Btree * access method which needs the previous page). We use the PREV_PGNO * field to store our own page number. */ #define ADDR(P) (PREV_PGNO((P))) /* Extract key/data offsets and data for a given index. */ #define DATA_OFF(P, N) \ REFERENCE(P, indx_t, PAGE_OVERHEAD + N * PAIR_OVERHEAD + sizeof(indx_t)) #define KEY_OFF(P, N) \ REFERENCE(P, indx_t, PAGE_OVERHEAD + N * PAIR_OVERHEAD) #define KEY(P, N) (((PAGE8 *)(P)) + KEY_OFF((P), (N))) #define DATA(P, N) (((PAGE8 *)(P)) + DATA_OFF((P), (N))) /* * Macros used to compute various sizes on a page. */ #define PAIRSIZE(K, D) (PAIR_OVERHEAD + (K)->size + (D)->size) #define BIGOVERHEAD (4 * sizeof(u_int16_t)) #define KEYSIZE(K) (4 * sizeof(u_int16_t) + (K)->size); #define OVFLSIZE (2 * sizeof(u_int16_t)) #define BIGPAGEOVERHEAD (4 * sizeof(u_int16_t)) #define BIGPAGEOFFSET 4 #define BIGPAGESIZE(P) ((P)->BSIZE - BIGPAGEOVERHEAD) #define PAGE_META(N) (((N) + 3) * sizeof(u_int16_t)) #define MINFILL 0.75 #define ISBIG(N, P) (((N) > ((P)->hdr.bsize * MINFILL)) ? 1 : 0) #define ITEMSIZE(I) (sizeof(u_int16_t) + (I)->size) /* * Big key/data pages use a different page format. They have a single * key/data "pair" containing the length of the key and data instead * of offsets. */ #define BIGKEYLEN(P) (KEY_OFF((P), 0)) #define BIGDATALEN(P) (DATA_OFF((P), 0)) #define BIGKEY(P) (((PAGE8 *)(P)) + PAGE_OVERHEAD + PAIR_OVERHEAD) #define BIGDATA(P) \ (((PAGE8 *)(P)) + PAGE_OVERHEAD + PAIR_OVERHEAD + KEY_OFF((P), 0)) #define OVFLPAGE 0 #define BIGPAIR 0 #define INVALID_PGNO 0xFFFFFFFF typedef unsigned short PAGE16; typedef unsigned char PAGE8; #define A_BUCKET 0 #define A_OVFL 1 #define A_BITMAP 2 #define A_RAW 4 #define A_HEADER 5 #define PAIRFITS(P,K,D) ((PAIRSIZE((K),(D))) <= FREESPACE((P))) #define BIGPAIRFITS(P) ((FREESPACE((P)) >= PAIR_OVERHEAD)) /* * Since these are all unsigned, we need to guarantee that we never go * negative. Offset values are 0-based and overheads are one based (i.e. * one byte of overhead is 1, not 0), so we need to convert OFFSETs to * 1-based counting before subtraction. */ #define FREESPACE(P) \ ((OFFSET((P)) + 1 - PAGE_OVERHEAD - (NUM_ENT((P)) * PAIR_OVERHEAD))) /* * Overhead on header pages is just one word -- the length of the * header info stored on that page. */ #define HEADER_OVERHEAD 4 #define HASH_PAGE 2 #define HASH_BIGPAGE 3 #define HASH_OVFLPAGE 4 #ifdef __cplusplus } #endif #endif /* !_KRB5_DB2_PAGE_H */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KRB5_DB2_SEARCH_H #define _KRB5_DB2_SEARCH_H #ifdef __cplusplus extern "C" { #endif /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)search.h 8.1 (Berkeley) 6/4/93 */ /* Backward compatibility to hsearch interface. */ typedef struct entry { char *key; char *data; } ENTRY; typedef enum { FIND, ENTER } ACTION; #define hcreate kdb2_hcreate #define hdestroy kdb2_hdestroy #define hsearch kdb2_hsearch int hcreate __P((unsigned int)); void hdestroy __P((void)); ENTRY *hsearch __P((ENTRY, ACTION)); #ifdef __cplusplus } #endif #endif /* !_KRB5_DB2_SEARCH_H */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KRB5_DB2_MISC_H #define _KRB5_DB2_MISC_H #ifdef __cplusplus extern "C" { #endif /* * Copyright (c) 1991, 1993 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)compat.h 8.13 (Berkeley) 2/21/94 */ #ifndef _DB_INT_H_ #define _DB_INT_H_ #include "db.h" /* deal with autoconf-based stuff */ #define DB_LITTLE_ENDIAN 1234 #define DB_BIG_ENDIAN 4321 #include #ifdef HAVE_ENDIAN_H # include #endif #ifdef HAVE_MACHINE_ENDIAN_H # include #endif #ifdef HAVE_SYS_PARAM_H # include #endif /* SUNW14resync: The following code is disabled as it correctly determines the endianness of the system. This would break backward compatability for x86 as prior to this resync all architectures are treated similarily - as big endian. See definition of "WORDS_BIGENDIAN" in db-config.h. */ #if 0 /* Handle both BIG and LITTLE defined and BYTE_ORDER matches one, or just one defined; both with and without leading underscores. Ignore "PDP endian" machines, this code doesn't support them anyways. */ #if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) && !defined(BYTE_ORDER) # ifdef __LITTLE_ENDIAN__ # define LITTLE_ENDIAN __LITTLE_ENDIAN__ # endif # ifdef __BIG_ENDIAN__ # define BIG_ENDIAN __BIG_ENDIAN__ # endif #endif #if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) && !defined(BYTE_ORDER) # ifdef _LITTLE_ENDIAN # define LITTLE_ENDIAN _LITTLE_ENDIAN # endif # ifdef _BIG_ENDIAN # define BIG_ENDIAN _BIG_ENDIAN # endif # ifdef _BYTE_ORDER # define BYTE_ORDER _BYTE_ORDER # endif #endif #if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) && !defined(BYTE_ORDER) # ifdef __LITTLE_ENDIAN # define LITTLE_ENDIAN __LITTLE_ENDIAN # endif # ifdef __BIG_ENDIAN # define BIG_ENDIAN __BIG_ENDIAN # endif # ifdef __BYTE_ORDER # define BYTE_ORDER __BYTE_ORDER # endif #endif #if defined(_MIPSEL) && !defined(LITTLE_ENDIAN) # define LITTLE_ENDIAN #endif #if defined(_MIPSEB) && !defined(BIG_ENDIAN) # define BIG_ENDIAN #endif #if defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) && defined(BYTE_ORDER) # if LITTLE_ENDIAN == BYTE_ORDER # define DB_BYTE_ORDER DB_LITTLE_ENDIAN # elif BIG_ENDIAN == BYTE_ORDER # define DB_BYTE_ORDER DB_BIG_ENDIAN # else # error "LITTLE_ENDIAN and BIG_ENDIAN defined, but can't determine byte order" # endif #elif defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) # define DB_BYTE_ORDER DB_LITTLE_ENDIAN #elif defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN) # define DB_BYTE_ORDER DB_BIG_ENDIAN #else # error "can't determine byte order from included system headers" #endif #endif #ifdef WORDS_BIGENDIAN #define DB_BYTE_ORDER DB_BIG_ENDIAN #else #define DB_BYTE_ORDER DB_LITTLE_ENDIAN #endif /* end autoconf-based stuff */ /* include necessary system header files */ #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #ifdef HAVE_STDINT_H #include #endif #ifdef HAVE_INTTYPES_H /* Tru64 5.1: int8_t is defined here, and stdint.h doesn't exist. */ #include #endif #include #include #include /* types and constants used for database structure */ #define MAX_PAGE_NUMBER 0xffffffff /* >= # of pages in a file */ typedef u_int32_t db_pgno_t; #define MAX_PAGE_OFFSET 65535 /* >= # of bytes in a page */ typedef u_int16_t indx_t; #define MAX_REC_NUMBER 0xffffffff /* >= # of records in a tree */ typedef u_int32_t recno_t; /* * Little endian <==> big endian 32-bit swap macros. * M_32_SWAP swap a memory location * P_32_SWAP swap a referenced memory location * P_32_COPY swap from one location to another */ #define M_32_SWAP(a) { \ u_int32_t _tmp = a; \ ((char *)&a)[0] = ((char *)&_tmp)[3]; \ ((char *)&a)[1] = ((char *)&_tmp)[2]; \ ((char *)&a)[2] = ((char *)&_tmp)[1]; \ ((char *)&a)[3] = ((char *)&_tmp)[0]; \ } #define P_32_SWAP(a) { \ u_int32_t _tmp = *(u_int32_t *)a; \ ((char *)a)[0] = ((char *)&_tmp)[3]; \ ((char *)a)[1] = ((char *)&_tmp)[2]; \ ((char *)a)[2] = ((char *)&_tmp)[1]; \ ((char *)a)[3] = ((char *)&_tmp)[0]; \ } #define P_32_COPY(a, b) { \ ((char *)&(b))[0] = ((char *)&(a))[3]; \ ((char *)&(b))[1] = ((char *)&(a))[2]; \ ((char *)&(b))[2] = ((char *)&(a))[1]; \ ((char *)&(b))[3] = ((char *)&(a))[0]; \ } /* * Little endian <==> big endian 16-bit swap macros. * M_16_SWAP swap a memory location * P_16_SWAP swap a referenced memory location * P_16_COPY swap from one location to another */ #define M_16_SWAP(a) { \ u_int16_t _tmp = a; \ ((char *)&a)[0] = ((char *)&_tmp)[1]; \ ((char *)&a)[1] = ((char *)&_tmp)[0]; \ } #define P_16_SWAP(a) { \ u_int16_t _tmp = *(u_int16_t *)a; \ ((char *)a)[0] = ((char *)&_tmp)[1]; \ ((char *)a)[1] = ((char *)&_tmp)[0]; \ } #define P_16_COPY(a, b) { \ ((char *)&(b))[0] = ((char *)&(a))[1]; \ ((char *)&(b))[1] = ((char *)&(a))[0]; \ } /* open functions for each database type, used in dbopen() */ #define __bt_open __kdb2_bt_open #define __hash_open __kdb2_hash_open #define __rec_open __kdb2_rec_open #define __dbpanic __kdb2_dbpanic DB *__bt_open __P((const char *, int, int, const BTREEINFO *, int)); DB *__hash_open __P((const char *, int, int, const HASHINFO *, int)); DB *__rec_open __P((const char *, int, int, const RECNOINFO *, int)); void __dbpanic __P((DB *dbp)); /* * There is no portable way to figure out the maximum value of a file * offset, so we put it here. */ #ifdef OFF_T_MAX #define DB_OFF_T_MAX OFF_T_MAX #else #define DB_OFF_T_MAX LONG_MAX #endif #ifndef O_ACCMODE /* POSIX 1003.1 access mode mask. */ #define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) #endif /* * If you can't provide lock values in the open(2) call. Note, this * allows races to happen. */ #ifndef O_EXLOCK /* 4.4BSD extension. */ #define O_EXLOCK 0 #endif #ifndef O_SHLOCK /* 4.4BSD extension. */ #define O_SHLOCK 0 #endif #ifndef EFTYPE #define EFTYPE EINVAL /* POSIX 1003.1 format errno. */ #endif #ifndef STDERR_FILENO #define STDIN_FILENO 0 /* ANSI C #defines */ #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #endif #ifndef SEEK_END #define SEEK_SET 0 /* POSIX 1003.1 seek values */ #define SEEK_CUR 1 #define SEEK_END 2 #endif #ifndef NULL /* ANSI C #defines NULL everywhere. */ #define NULL 0 #endif #ifndef MAX /* Usually found in . */ #define MAX(_a,_b) ((_a)<(_b)?(_b):(_a)) #endif #ifndef MIN /* Usually found in . */ #define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) #endif #ifndef S_ISDIR /* POSIX 1003.1 file type tests. */ #define S_ISDIR(m) ((m & 0170000) == 0040000) /* directory */ #define S_ISCHR(m) ((m & 0170000) == 0020000) /* char special */ #define S_ISBLK(m) ((m & 0170000) == 0060000) /* block special */ #define S_ISREG(m) ((m & 0170000) == 0100000) /* regular file */ #define S_ISFIFO(m) ((m & 0170000) == 0010000) /* fifo */ #endif #ifndef S_ISLNK /* BSD POSIX 1003.1 extensions */ #define S_ISLNK(m) ((m & 0170000) == 0120000) /* symbolic link */ #define S_ISSOCK(m) ((m & 0170000) == 0140000) /* socket */ #endif #ifndef O_BINARY #define O_BINARY 0 /* Needed for Win32 compiles */ #endif #endif /* _DB_INT_H_ */ #ifdef __cplusplus } #endif #endif /* !_KRB5_DB2_MISC_H */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)ndbm.h 8.1 (Berkeley) 6/2/93 */ #ifndef _NDBM_H_ #define _NDBM_H_ #include "db.h" /* Map dbm interface onto db(3). */ #define DBM_RDONLY O_RDONLY /* Flags to dbm_store(). */ #define DBM_INSERT 0 #define DBM_REPLACE 1 /* * The db(3) support for ndbm(3) always appends this suffix to the * file name to avoid overwriting the user's original database. */ #define DBM_SUFFIX ".db" typedef struct { char *dptr; int dsize; } datum; typedef DB DBM; #define dbm_pagfno(a) DBM_PAGFNO_NOT_AVAILABLE #define dbm_close kdb2_dbm_close #define dbm_delete kdb2_dbm_delete #define dbm_fetch kdb2_dbm_fetch #define dbm_firstkey kdb2_dbm_firstkey #define dbm_forder kdb2_dbm_forder #define dbm_nextkey kdb2_dbm_nextkey #define dbm_open kdb2_dbm_open #define dbm_store kdb2_dbm_store #define dbm_dirfno kdb2_dbm_dirfno #define dbm_error kdb2_dbm_error #define dbm_clearerr kdb2_dbm_clearerr __BEGIN_DECLS void dbm_close __P((DBM *)); int dbm_delete __P((DBM *, datum)); datum dbm_fetch __P((DBM *, datum)); datum dbm_firstkey __P((DBM *)); long dbm_forder __P((DBM *, datum)); datum dbm_nextkey __P((DBM *)); DBM *dbm_open __P((const char *, int, int)); int dbm_store __P((DBM *, datum, datum, int)); int dbm_dirfno __P((DBM *)); int dbm_error __P((DBM *db)); int dbm_clearerr __P((DBM *db)); __END_DECLS #endif /* !_NDBM_H_ */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KRB5_DB2_DBQUEUE_H #define _KRB5_DB2_DBQUEUE_H #ifdef __cplusplus extern "C" { #endif /* * Copyright (c) 1991, 1993 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)queue.h 8.3 (Berkeley) 12/13/93 */ #ifndef _QUEUE_H_ #define _QUEUE_H_ /* * This file defines three types of data structures: lists, tail queues, * and circular queues. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list after * an existing element or at the head of the list. A list may only be * traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list after * an existing element, at the head of the list, or at the end of the * list. A tail queue may only be traversed in the forward direction. * * A circle queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the list. * A circle queue may be traversed in either direction, but has a more * complex end of list detection. * * For details on the use of these macros, see the queue(3) manual page. */ /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List functions. */ #define LIST_INIT(head) { \ (head)->lh_first = NULL; \ } #define LIST_INSERT_AFTER(listelm, elm, field) { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } #define LIST_INSERT_HEAD(head, elm, field) { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } #define LIST_REMOVE(elm, field) { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ } /* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* * Tail queue functions. */ #define TAILQ_INIT(head) { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } #define TAILQ_INSERT_HEAD(head, elm, field) { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } #define TAILQ_INSERT_TAIL(head, elm, field) { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } #define TAILQ_INSERT_AFTER(head, listelm, elm, field) { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } #define TAILQ_REMOVE(head, elm, field) { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ } /* * Circular queue definitions. */ #define CIRCLEQ_HEAD(name, type) \ struct name { \ struct type *cqh_first; /* first element */ \ struct type *cqh_last; /* last element */ \ } #define CIRCLEQ_ENTRY(type) \ struct { \ struct type *cqe_next; /* next element */ \ struct type *cqe_prev; /* previous element */ \ } /* * Circular queue functions. */ #define CIRCLEQ_INIT(head) { \ (head)->cqh_first = (void *)(head); \ (head)->cqh_last = (void *)(head); \ } #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) { \ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ (elm)->field.cqe_prev = (listelm); \ if ((listelm)->field.cqe_next == (void *)(head)) \ (head)->cqh_last = (elm); \ else \ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ (listelm)->field.cqe_next = (elm); \ } #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) { \ (elm)->field.cqe_next = (listelm); \ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ if ((listelm)->field.cqe_prev == (void *)(head)) \ (head)->cqh_first = (elm); \ else \ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ (listelm)->field.cqe_prev = (elm); \ } #define CIRCLEQ_INSERT_HEAD(head, elm, field) { \ (elm)->field.cqe_next = (head)->cqh_first; \ (elm)->field.cqe_prev = (void *)(head); \ if ((head)->cqh_last == (void *)(head)) \ (head)->cqh_last = (elm); \ else \ (head)->cqh_first->field.cqe_prev = (elm); \ (head)->cqh_first = (elm); \ } #define CIRCLEQ_INSERT_TAIL(head, elm, field) { \ (elm)->field.cqe_next = (void *)(head); \ (elm)->field.cqe_prev = (head)->cqh_last; \ if ((head)->cqh_first == (void *)(head)) \ (head)->cqh_first = (elm); \ else \ (head)->cqh_last->field.cqe_next = (elm); \ (head)->cqh_last = (elm); \ } #define CIRCLEQ_REMOVE(head, elm, field) { \ if ((elm)->field.cqe_next == (void *)(head)) \ (head)->cqh_last = (elm)->field.cqe_prev; \ else \ (elm)->field.cqe_next->field.cqe_prev = \ (elm)->field.cqe_prev; \ if ((elm)->field.cqe_prev == (void *)(head)) \ (head)->cqh_first = (elm)->field.cqe_next; \ else \ (elm)->field.cqe_prev->field.cqe_next = \ (elm)->field.cqe_next; \ } #endif /* !_QUEUE_H_ */ #ifdef __cplusplus } #endif #endif /* !_KRB5_DB2_DBQUEUE_H */ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # # Due to mistakes made early in the history of this library, there are # no SUNW_1.1 symbols, but the version is now kept as a placeholder. # Don't add any symbols to this version. $mapfile_version 2 SYMBOL_VERSION SUNW_1.1 { global: SUNW_1.1; }; SYMBOL_VERSION SUNWprivate_1.1 { global: kdb2_bt_rseq; kdb2_dbm_clearerr; kdb2_dbm_close; kdb2_dbm_delete; kdb2_dbm_dirfno; kdb2_dbm_error; kdb2_dbm_fetch; kdb2_dbm_firstkey; kdb2_dbm_nextkey; kdb2_dbm_open; kdb2_dbm_store; kdb2_dbminit; kdb2_dbopen; kdb2_delete; kdb2_fetch; kdb2_firstkey; kdb2_hcreate; kdb2_hdestroy; kdb2_hsearch; kdb2_mpool_close; kdb2_mpool_delete; kdb2_mpool_filter; kdb2_mpool_get; kdb2_mpool_new; kdb2_mpool_open; kdb2_mpool_put; kdb2_mpool_sync; kdb2_nextkey; kdb2_store; local: *; }; /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)mpool.c 8.7 (Berkeley) 11/2/95"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include #include #include "db-int.h" #include "mpool.h" static BKT *mpool_bkt __P((MPOOL *)); static BKT *mpool_look __P((MPOOL *, db_pgno_t)); static int mpool_write __P((MPOOL *, BKT *)); /* * mpool_open -- * Initialize a memory pool. */ MPOOL * mpool_open(key, fd, pagesize, maxcache) void *key; int fd; db_pgno_t pagesize, maxcache; { struct stat sb; MPOOL *mp; int entry; /* * Get information about the file. * * XXX * We don't currently handle pipes, although we should. */ if (fstat(fd, &sb)) return (NULL); if (!S_ISREG(sb.st_mode)) { errno = ESPIPE; return (NULL); } /* Allocate and initialize the MPOOL cookie. */ if ((mp = (MPOOL *)calloc(1, sizeof(MPOOL))) == NULL) return (NULL); CIRCLEQ_INIT(&mp->lqh); for (entry = 0; entry < HASHSIZE; ++entry) CIRCLEQ_INIT(&mp->hqh[entry]); mp->maxcache = maxcache; mp->npages = sb.st_size / pagesize; mp->pagesize = pagesize; mp->fd = fd; return (mp); } /* * mpool_filter -- * Initialize input/output filters. */ void mpool_filter(mp, pgin, pgout, pgcookie) MPOOL *mp; void (*pgin) __P((void *, db_pgno_t, void *)); void (*pgout) __P((void *, db_pgno_t, void *)); void *pgcookie; { mp->pgin = pgin; mp->pgout = pgout; mp->pgcookie = pgcookie; } /* * mpool_new -- * Get a new page of memory. */ void * mpool_new(mp, pgnoaddr, flags) MPOOL *mp; db_pgno_t *pgnoaddr; u_int flags; { struct _hqh *head; BKT *bp; if (mp->npages == MAX_PAGE_NUMBER) { (void)fprintf(stderr, "mpool_new: page allocation overflow.\n"); abort(); } #ifdef STATISTICS ++mp->pagenew; #endif /* * Get a BKT from the cache. Assign a new page number, attach * it to the head of the hash chain, the tail of the lru chain, * and return. */ if ((bp = mpool_bkt(mp)) == NULL) return (NULL); if (flags == MPOOL_PAGE_REQUEST) { mp->npages++; bp->pgno = *pgnoaddr; } else bp->pgno = *pgnoaddr = mp->npages++; bp->flags = MPOOL_PINNED | MPOOL_INUSE; head = &mp->hqh[HASHKEY(bp->pgno)]; CIRCLEQ_INSERT_HEAD(head, bp, hq); CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q); return (bp->page); } int mpool_delete(mp, page) MPOOL *mp; void *page; { struct _hqh *head; BKT *bp; bp = (BKT *)((char *)page - sizeof(BKT)); #ifdef DEBUG if (!(bp->flags & MPOOL_PINNED)) { (void)fprintf(stderr, "mpool_delete: page %d not pinned\n", bp->pgno); abort(); } #endif /* Remove from the hash and lru queues. */ head = &mp->hqh[HASHKEY(bp->pgno)]; CIRCLEQ_REMOVE(head, bp, hq); CIRCLEQ_REMOVE(&mp->lqh, bp, q); free(bp); return (RET_SUCCESS); } /* * mpool_get * Get a page. */ void * mpool_get(mp, pgno, flags) MPOOL *mp; db_pgno_t pgno; u_int flags; /* XXX not used? */ { struct _hqh *head; BKT *bp; off_t off; int nr; #ifdef STATISTICS ++mp->pageget; #endif /* Check for a page that is cached. */ if ((bp = mpool_look(mp, pgno)) != NULL) { #ifdef DEBUG if (!(flags & MPOOL_IGNOREPIN) && bp->flags & MPOOL_PINNED) { (void)fprintf(stderr, "mpool_get: page %d already pinned\n", bp->pgno); abort(); } #endif /* * Move the page to the head of the hash chain and the tail * of the lru chain. */ head = &mp->hqh[HASHKEY(bp->pgno)]; CIRCLEQ_REMOVE(head, bp, hq); CIRCLEQ_INSERT_HEAD(head, bp, hq); CIRCLEQ_REMOVE(&mp->lqh, bp, q); CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q); /* Return a pinned page. */ bp->flags |= MPOOL_PINNED; return (bp->page); } /* Get a page from the cache. */ if ((bp = mpool_bkt(mp)) == NULL) return (NULL); /* Read in the contents. */ #ifdef STATISTICS ++mp->pageread; #endif off = mp->pagesize * pgno; if (off / mp->pagesize != pgno) { /* Run past the end of the file, or at least the part we can address without large-file support? */ errno = E2BIG; return NULL; } if (lseek(mp->fd, off, SEEK_SET) != off) return (NULL); if ((nr = read(mp->fd, bp->page, mp->pagesize)) != mp->pagesize) { if (nr > 0) { /* A partial read is definitely bad. */ errno = EINVAL; return (NULL); } else { /* * A zero-length reads, means you need to create a * new page. */ memset(bp->page, 0, mp->pagesize); } } /* Set the page number, pin the page. */ bp->pgno = pgno; if (!(flags & MPOOL_IGNOREPIN)) bp->flags = MPOOL_PINNED; bp->flags |= MPOOL_INUSE; /* * Add the page to the head of the hash chain and the tail * of the lru chain. */ head = &mp->hqh[HASHKEY(bp->pgno)]; CIRCLEQ_INSERT_HEAD(head, bp, hq); CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q); /* Run through the user's filter. */ if (mp->pgin != NULL) (mp->pgin)(mp->pgcookie, bp->pgno, bp->page); return (bp->page); } /* * mpool_put * Return a page. */ int mpool_put(mp, page, flags) MPOOL *mp; void *page; u_int flags; { BKT *bp; #ifdef STATISTICS ++mp->pageput; #endif bp = (BKT *)((char *)page - sizeof(BKT)); #ifdef DEBUG if (!(bp->flags & MPOOL_PINNED)) { (void)fprintf(stderr, "mpool_put: page %d not pinned\n", bp->pgno); abort(); } #endif bp->flags &= ~MPOOL_PINNED; if (flags & MPOOL_DIRTY) bp->flags |= flags & MPOOL_DIRTY; return (RET_SUCCESS); } /* * mpool_close * Close the buffer pool. */ int mpool_close(mp) MPOOL *mp; { BKT *bp; /* Free up any space allocated to the lru pages. */ while ((bp = mp->lqh.cqh_first) != (void *)&mp->lqh) { CIRCLEQ_REMOVE(&mp->lqh, mp->lqh.cqh_first, q); free(bp); } /* Free the MPOOL cookie. */ free(mp); return (RET_SUCCESS); } /* * mpool_sync * Sync the pool to disk. */ int mpool_sync(mp) MPOOL *mp; { BKT *bp; /* Walk the lru chain, flushing any dirty pages to disk. */ for (bp = mp->lqh.cqh_first; bp != (void *)&mp->lqh; bp = bp->q.cqe_next) if (bp->flags & MPOOL_DIRTY && mpool_write(mp, bp) == RET_ERROR) return (RET_ERROR); /* Sync the file descriptor. */ return (fsync(mp->fd) ? RET_ERROR : RET_SUCCESS); } /* * mpool_bkt * Get a page from the cache (or create one). */ static BKT * mpool_bkt(mp) MPOOL *mp; { struct _hqh *head; BKT *bp; /* If under the max cached, always create a new page. */ if (mp->curcache < mp->maxcache) goto new; /* * If the cache is max'd out, walk the lru list for a buffer we * can flush. If we find one, write it (if necessary) and take it * off any lists. If we don't find anything we grow the cache anyway. * The cache never shrinks. */ for (bp = mp->lqh.cqh_first; bp != (void *)&mp->lqh; bp = bp->q.cqe_next) if (!(bp->flags & MPOOL_PINNED)) { /* Flush if dirty. */ if (bp->flags & MPOOL_DIRTY && mpool_write(mp, bp) == RET_ERROR) return (NULL); #ifdef STATISTICS ++mp->pageflush; #endif /* Remove from the hash and lru queues. */ head = &mp->hqh[HASHKEY(bp->pgno)]; CIRCLEQ_REMOVE(head, bp, hq); CIRCLEQ_REMOVE(&mp->lqh, bp, q); #ifdef DEBUG { void *spage; spage = bp->page; memset(bp, 0xff, sizeof(BKT) + mp->pagesize); bp->page = spage; } #endif bp->flags = 0; return (bp); } new: if ((bp = (BKT *)malloc(sizeof(BKT) + mp->pagesize)) == NULL) return (NULL); #ifdef STATISTICS ++mp->pagealloc; #endif #if defined(DEBUG) || defined(PURIFY) || 1 memset(bp, 0xff, sizeof(BKT) + mp->pagesize); #endif bp->page = (char *)bp + sizeof(BKT); bp->flags = 0; ++mp->curcache; return (bp); } /* * mpool_write * Write a page to disk. */ static int mpool_write(mp, bp) MPOOL *mp; BKT *bp; { off_t off; #ifdef STATISTICS ++mp->pagewrite; #endif /* Run through the user's filter. */ if (mp->pgout) (mp->pgout)(mp->pgcookie, bp->pgno, bp->page); off = mp->pagesize * bp->pgno; if (off / mp->pagesize != bp->pgno) { /* Run past the end of the file, or at least the part we can address without large-file support? */ errno = E2BIG; return RET_ERROR; } if (lseek(mp->fd, off, SEEK_SET) != off) return (RET_ERROR); if (write(mp->fd, bp->page, mp->pagesize) != mp->pagesize) return (RET_ERROR); bp->flags &= ~MPOOL_DIRTY; return (RET_SUCCESS); } /* * mpool_look * Lookup a page in the cache. */ static BKT * mpool_look(mp, pgno) MPOOL *mp; db_pgno_t pgno; { struct _hqh *head; BKT *bp; head = &mp->hqh[HASHKEY(pgno)]; for (bp = head->cqh_first; bp != (void *)head; bp = bp->hq.cqe_next) if ((bp->pgno == pgno) && (bp->flags & MPOOL_INUSE)) { #ifdef STATISTICS ++mp->cachehit; #endif return (bp); } #ifdef STATISTICS ++mp->cachemiss; #endif return (NULL); } #ifdef STATISTICS /* * mpool_stat * Print out cache statistics. */ void mpool_stat(mp) MPOOL *mp; { BKT *bp; int cnt; char *sep; (void)fprintf(stderr, "%lu pages in the file\n", mp->npages); (void)fprintf(stderr, "page size %lu, cacheing %lu pages of %lu page max cache\n", mp->pagesize, mp->curcache, mp->maxcache); (void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n", mp->pageput, mp->pageget, mp->pagenew); (void)fprintf(stderr, "%lu page allocs, %lu page flushes\n", mp->pagealloc, mp->pageflush); if (mp->cachehit + mp->cachemiss) (void)fprintf(stderr, "%.0f%% cache hit rate (%lu hits, %lu misses)\n", ((double)mp->cachehit / (mp->cachehit + mp->cachemiss)) * 100, mp->cachehit, mp->cachemiss); (void)fprintf(stderr, "%lu page reads, %lu page writes\n", mp->pageread, mp->pagewrite); sep = ""; cnt = 0; for (bp = mp->lqh.cqh_first; bp != (void *)&mp->lqh; bp = bp->q.cqe_next) { (void)fprintf(stderr, "%s%d", sep, bp->pgno); if (bp->flags & MPOOL_DIRTY) (void)fprintf(stderr, "d"); if (bp->flags & MPOOL_PINNED) (void)fprintf(stderr, "P"); if (++cnt == 10) { sep = "\n"; cnt = 0; } else sep = ", "; } (void)fprintf(stderr, "\n"); } #endif /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KRB5_DB2_MPOOL_MPOOL_H #define _KRB5_DB2_MPOOL_MPOOL_H #ifdef __cplusplus extern "C" { #endif /* * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)mpool.h 8.4 (Berkeley) 11/2/95 */ #include "db-queue.h" /* * The memory pool scheme is a simple one. Each in-memory page is referenced * by a bucket which is threaded in up to two of three ways. All active pages * are threaded on a hash chain (hashed by page number) and an lru chain. * Inactive pages are threaded on a free chain. Each reference to a memory * pool is handed an opaque MPOOL cookie which stores all of this information. */ #define HASHSIZE 128 #define HASHKEY(pgno) ((pgno - 1) % HASHSIZE) /* The BKT structures are the elements of the queues. */ typedef struct _bkt { CIRCLEQ_ENTRY(_bkt) hq; /* hash queue */ CIRCLEQ_ENTRY(_bkt) q; /* lru queue */ void *page; /* page */ db_pgno_t pgno; /* page number */ #define MPOOL_DIRTY 0x01 /* page needs to be written */ #define MPOOL_PINNED 0x02 /* page is pinned into memory */ #define MPOOL_INUSE 0x04 /* page address is valid */ u_int8_t flags; /* flags */ } BKT; typedef struct MPOOL { CIRCLEQ_HEAD(_lqh, _bkt) lqh; /* lru queue head */ /* hash queue array */ CIRCLEQ_HEAD(_hqh, _bkt) hqh[HASHSIZE]; db_pgno_t curcache; /* current number of cached pages */ db_pgno_t maxcache; /* max number of cached pages */ db_pgno_t npages; /* number of pages in the file */ u_long pagesize; /* file page size */ int fd; /* file descriptor */ /* page in conversion routine */ void (*pgin) __P((void *, db_pgno_t, void *)); /* page out conversion routine */ void (*pgout) __P((void *, db_pgno_t, void *)); void *pgcookie; /* cookie for page in/out routines */ #ifdef STATISTICS u_long cachehit; u_long cachemiss; u_long pagealloc; u_long pageflush; u_long pageget; u_long pagenew; u_long pageput; u_long pageread; u_long pagewrite; #endif } MPOOL; #define MPOOL_IGNOREPIN 0x01 /* Ignore if the page is pinned. */ #define MPOOL_PAGE_REQUEST 0x01 /* Allocate a new page with a specific page number. */ #define MPOOL_PAGE_NEXT 0x02 /* Allocate a new page with the next page number. */ #define mpool_open kdb2_mpool_open #define mpool_filter kdb2_mpool_filter #define mpool_new kdb2_mpool_new #define mpool_get kdb2_mpool_get #define mpool_delete kdb2_mpool_delete #define mpool_put kdb2_mpool_put #define mpool_sync kdb2_mpool_sync #define mpool_close kdb2_mpool_close #define mpool_stat kdb2_mpool_stat __BEGIN_DECLS MPOOL *mpool_open __P((void *, int, db_pgno_t, db_pgno_t)); void mpool_filter __P((MPOOL *, void (*)(void *, db_pgno_t, void *), void (*)(void *, db_pgno_t, void *), void *)); void *mpool_new __P((MPOOL *, db_pgno_t *, u_int)); void *mpool_get __P((MPOOL *, db_pgno_t, u_int)); int mpool_delete __P((MPOOL *, void *)); int mpool_put __P((MPOOL *, void *, u_int)); int mpool_sync __P((MPOOL *)); int mpool_close __P((MPOOL *)); #ifdef STATISTICS void mpool_stat __P((MPOOL *)); #endif __END_DECLS #ifdef __cplusplus } #endif #endif /* !_KRB5_DB2_MPOOL_MPOOL_H */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KRB5_DB2_RECNO_EXTERN_H #define _KRB5_DB2_RECNO_EXTERN_H #ifdef __cplusplus extern "C" { #endif /* * Copyright (c) 1991, 1993 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)extern.h 8.3 (Berkeley) 6/4/94 */ #include "../btree/extern.h" #define __rec_close __kdb2_rec_close #define __rec_delete __kdb2_rec_delete #define __rec_dleaf __kdb2_rec_dleaf #define __rec_fd __kdb2_rec_fd #define __rec_fmap __kdb2_rec_fmap #define __rec_fout __kdb2_rec_fout #define __rec_fpipe __kdb2_rec_fpipe #define __rec_get __kdb2_rec_get #define __rec_iput __kdb2_rec_iput #define __rec_put __kdb2_rec_put #define __rec_ret __kdb2_rec_ret #define __rec_search __kdb2_rec_search #define __rec_seq __kdb2_rec_seq #define __rec_sync __kdb2_rec_sync #define __rec_vmap __kdb2_rec_vmap #define __rec_vout __kdb2_rec_vout #define __rec_vpipe __kdb2_rec_vpipe int __rec_close __P((DB *)); int __rec_delete __P((const DB *, const DBT *, u_int)); int __rec_dleaf __P((BTREE *, PAGE *, u_int32_t)); int __rec_fd __P((const DB *)); int __rec_fmap __P((BTREE *, recno_t)); int __rec_fout __P((BTREE *)); int __rec_fpipe __P((BTREE *, recno_t)); int __rec_get __P((const DB *, const DBT *, DBT *, u_int)); int __rec_iput __P((BTREE *, recno_t, const DBT *, u_int)); int __rec_put __P((const DB *dbp, DBT *, const DBT *, u_int)); int __rec_ret __P((BTREE *, EPG *, recno_t, DBT *, DBT *)); EPG *__rec_search __P((BTREE *, recno_t, enum SRCHOP)); int __rec_seq __P((const DB *, DBT *, DBT *, u_int)); int __rec_sync __P((const DB *, u_int)); int __rec_vmap __P((BTREE *, recno_t)); int __rec_vout __P((BTREE *)); int __rec_vpipe __P((BTREE *, recno_t)); #ifdef __cplusplus } #endif #endif /* !_KRB5_DB2_RECNO_EXTERN_H */ /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)rec_close.c 8.9 (Berkeley) 11/18/94"; #endif /* LIBC_SCCS and not lint */ #include #include #ifdef RECNO_USE_MMAP #include #endif #include #include #include #include #include "db-int.h" #include "recno.h" /* * __REC_CLOSE -- Close a recno tree. * * Parameters: * dbp: pointer to access method * * Returns: * RET_ERROR, RET_SUCCESS */ int __rec_close(dbp) DB *dbp; { BTREE *t; int status; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } if (__rec_sync(dbp, 0) == RET_ERROR) return (RET_ERROR); /* Committed to closing. */ status = RET_SUCCESS; #ifdef RECNO_USE_MMAP if (F_ISSET(t, R_MEMMAPPED) && munmap(t->bt_smap, t->bt_msize)) status = RET_ERROR; #endif if (!F_ISSET(t, R_INMEM)) { if (F_ISSET(t, R_CLOSEFP)) { if (fclose(t->bt_rfp)) status = RET_ERROR; } else if (close(t->bt_rfd)) status = RET_ERROR; } if (__bt_close(dbp) == RET_ERROR) status = RET_ERROR; return (status); } /* * __REC_SYNC -- sync the recno tree to disk. * * Parameters: * dbp: pointer to access method * * Returns: * RET_SUCCESS, RET_ERROR. */ int __rec_sync(dbp, flags) const DB *dbp; u_int flags; { struct iovec iov[2]; BTREE *t; DBT data, key; off_t off; recno_t scursor, trec; int status; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } if (flags == R_RECNOSYNC) return (__bt_sync(dbp, 0)); if (F_ISSET(t, R_RDONLY | R_INMEM) || !F_ISSET(t, R_MODIFIED)) return (RET_SUCCESS); /* Read any remaining records into the tree. */ if (!F_ISSET(t, R_EOF) && t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR) return (RET_ERROR); /* Rewind the file descriptor. */ if (lseek(t->bt_rfd, (off_t)0, SEEK_SET) != 0) return (RET_ERROR); /* Save the cursor. */ scursor = t->bt_cursor.rcursor; key.size = sizeof(recno_t); key.data = &trec; if (F_ISSET(t, R_FIXLEN)) { /* * We assume that fixed length records are all fixed length. * Any that aren't are either EINVAL'd or corrected by the * record put code. */ status = (dbp->seq)(dbp, &key, &data, R_FIRST); while (status == RET_SUCCESS) { if (write(t->bt_rfd, data.data, data.size) != data.size) return (RET_ERROR); status = (dbp->seq)(dbp, &key, &data, R_NEXT); } } else { iov[1].iov_base = (char *)&t->bt_bval; iov[1].iov_len = 1; status = (dbp->seq)(dbp, &key, &data, R_FIRST); while (status == RET_SUCCESS) { iov[0].iov_base = data.data; iov[0].iov_len = data.size; if (writev(t->bt_rfd, iov, 2) != data.size + 1) return (RET_ERROR); status = (dbp->seq)(dbp, &key, &data, R_NEXT); } } /* Restore the cursor. */ t->bt_cursor.rcursor = scursor; if (status == RET_ERROR) return (RET_ERROR); if ((off = lseek(t->bt_rfd, (off_t)0, SEEK_CUR)) == -1) return (RET_ERROR); if (ftruncate(t->bt_rfd, off)) return (RET_ERROR); F_CLR(t, R_MODIFIED); return (RET_SUCCESS); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)rec_delete.c 8.7 (Berkeley) 7/14/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include "db-int.h" #include "recno.h" static int rec_rdelete __P((BTREE *, recno_t)); /* * __REC_DELETE -- Delete the item(s) referenced by a key. * * Parameters: * dbp: pointer to access method * key: key to delete * flags: R_CURSOR if deleting what the cursor references * * Returns: * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. */ int __rec_delete(dbp, key, flags) const DB *dbp; const DBT *key; u_int flags; { BTREE *t; recno_t nrec; int status; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } switch(flags) { case 0: if ((nrec = *(recno_t *)key->data) == 0) goto einval; if (nrec > t->bt_nrecs) return (RET_SPECIAL); --nrec; status = rec_rdelete(t, nrec); break; case R_CURSOR: if (!F_ISSET(&t->bt_cursor, CURS_INIT)) goto einval; if (t->bt_nrecs == 0) return (RET_SPECIAL); status = rec_rdelete(t, t->bt_cursor.rcursor - 1); if (status == RET_SUCCESS) --t->bt_cursor.rcursor; break; default: einval: errno = EINVAL; return (RET_ERROR); } if (status == RET_SUCCESS) F_SET(t, B_MODIFIED | R_MODIFIED); return (status); } /* * REC_RDELETE -- Delete the data matching the specified key. * * Parameters: * tree: tree * nrec: record to delete * * Returns: * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. */ static int rec_rdelete(t, nrec) BTREE *t; recno_t nrec; { EPG *e; PAGE *h; int status; /* Find the record; __rec_search pins the page. */ if ((e = __rec_search(t, nrec, SDELETE)) == NULL) return (RET_ERROR); /* Delete the record. */ h = e->page; status = __rec_dleaf(t, h, e->index); if (status != RET_SUCCESS) { mpool_put(t->bt_mp, h, 0); return (status); } mpool_put(t->bt_mp, h, MPOOL_DIRTY); return (RET_SUCCESS); } /* * __REC_DLEAF -- Delete a single record from a recno leaf page. * * Parameters: * t: tree * idx: index on current page to delete * * Returns: * RET_SUCCESS, RET_ERROR. */ int __rec_dleaf(t, h, idx) BTREE *t; PAGE *h; u_int32_t idx; { RLEAF *rl; indx_t *ip, cnt, offset; u_int32_t nbytes; char *from; void *to; /* * Delete a record from a recno leaf page. Internal records are never * deleted from internal pages, regardless of the records that caused * them to be added being deleted. Pages made empty by deletion are * not reclaimed. They are, however, made available for reuse. * * Pack the remaining entries at the end of the page, shift the indices * down, overwriting the deleted record and its index. If the record * uses overflow pages, make them available for reuse. */ to = rl = GETRLEAF(h, idx); if (rl->flags & P_BIGDATA && __ovfl_delete(t, rl->bytes) == RET_ERROR) return (RET_ERROR); nbytes = NRLEAF(rl); /* * Compress the key/data pairs. Compress and adjust the [BR]LEAF * offsets. Reset the headers. */ from = (char *)h + h->upper; memmove(from + nbytes, from, (char *)to - from); h->upper += nbytes; offset = h->linp[idx]; for (cnt = &h->linp[idx] - (ip = &h->linp[0]); cnt--; ++ip) if (ip[0] < offset) ip[0] += nbytes; for (cnt = &h->linp[NEXTINDEX(h)] - ip; --cnt; ++ip) ip[0] = ip[1] < offset ? ip[1] + nbytes : ip[1]; h->lower -= sizeof(indx_t); --t->bt_nrecs; return (RET_SUCCESS); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)rec_get.c 8.9 (Berkeley) 8/18/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include #include #include "db-int.h" #include "recno.h" /* * __REC_GET -- Get a record from the btree. * * Parameters: * dbp: pointer to access method * key: key to find * data: data to return * flag: currently unused * * Returns: * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. */ int __rec_get(dbp, key, data, flags) const DB *dbp; const DBT *key; DBT *data; u_int flags; { BTREE *t; EPG *e; recno_t nrec; int status; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } /* Get currently doesn't take any flags, and keys of 0 are illegal. */ if (flags || (nrec = *(recno_t *)key->data) == 0) { errno = EINVAL; return (RET_ERROR); } /* * If we haven't seen this record yet, try to find it in the * original file. */ if (nrec > t->bt_nrecs) { if (F_ISSET(t, R_EOF | R_INMEM)) return (RET_SPECIAL); if ((status = t->bt_irec(t, nrec)) != RET_SUCCESS) return (status); } --nrec; if ((e = __rec_search(t, nrec, SEARCH)) == NULL) return (RET_ERROR); status = __rec_ret(t, e, 0, NULL, data); if (F_ISSET(t, B_DB_LOCK)) mpool_put(t->bt_mp, e->page, 0); else t->bt_pinned = e->page; return (status); } /* * __REC_FPIPE -- Get fixed length records from a pipe. * * Parameters: * t: tree * cnt: records to read * * Returns: * RET_ERROR, RET_SUCCESS */ int __rec_fpipe(t, top) BTREE *t; recno_t top; { DBT data; recno_t nrec; size_t len; int ch; u_char *p; if (t->bt_rdata.size < t->bt_reclen) { t->bt_rdata.data = t->bt_rdata.data == NULL ? malloc(t->bt_reclen) : realloc(t->bt_rdata.data, t->bt_reclen); if (t->bt_rdata.data == NULL) return (RET_ERROR); t->bt_rdata.size = t->bt_reclen; } data.data = t->bt_rdata.data; data.size = t->bt_reclen; for (nrec = t->bt_nrecs; nrec < top;) { len = t->bt_reclen; for (p = t->bt_rdata.data;; *p++ = ch) if ((ch = getc(t->bt_rfp)) == EOF || !--len) { if (ch != EOF) *p = ch; if (len != 0) memset(p, t->bt_bval, len); if (__rec_iput(t, nrec, &data, 0) != RET_SUCCESS) return (RET_ERROR); ++nrec; break; } if (ch == EOF) break; } if (nrec < top) { F_SET(t, R_EOF); return (RET_SPECIAL); } return (RET_SUCCESS); } /* * __REC_VPIPE -- Get variable length records from a pipe. * * Parameters: * t: tree * cnt: records to read * * Returns: * RET_ERROR, RET_SUCCESS */ int __rec_vpipe(t, top) BTREE *t; recno_t top; { DBT data; recno_t nrec; indx_t len; size_t sz; int bval, ch; u_char *p; bval = t->bt_bval; for (nrec = t->bt_nrecs; nrec < top; ++nrec) { for (p = t->bt_rdata.data, sz = t->bt_rdata.size;; *p++ = ch, --sz) { if ((ch = getc(t->bt_rfp)) == EOF || ch == bval) { data.data = t->bt_rdata.data; data.size = p - (u_char *)t->bt_rdata.data; if (ch == EOF && data.size == 0) break; if (__rec_iput(t, nrec, &data, 0) != RET_SUCCESS) return (RET_ERROR); break; } if (sz == 0) { len = p - (u_char *)t->bt_rdata.data; t->bt_rdata.size += (sz = 256); t->bt_rdata.data = t->bt_rdata.data == NULL ? malloc(t->bt_rdata.size) : realloc(t->bt_rdata.data, t->bt_rdata.size); if (t->bt_rdata.data == NULL) return (RET_ERROR); p = (u_char *)t->bt_rdata.data + len; } } if (ch == EOF) break; } if (nrec < top) { F_SET(t, R_EOF); return (RET_SPECIAL); } return (RET_SUCCESS); } /* * __REC_FMAP -- Get fixed length records from a file. * * Parameters: * t: tree * cnt: records to read * * Returns: * RET_ERROR, RET_SUCCESS */ int __rec_fmap(t, top) BTREE *t; recno_t top; { DBT data; recno_t nrec; u_char *sp, *ep, *p; size_t len; if (t->bt_rdata.size < t->bt_reclen) { t->bt_rdata.data = t->bt_rdata.data == NULL ? malloc(t->bt_reclen) : realloc(t->bt_rdata.data, t->bt_reclen); if (t->bt_rdata.data == NULL) return (RET_ERROR); t->bt_rdata.size = t->bt_reclen; } data.data = t->bt_rdata.data; data.size = t->bt_reclen; sp = (u_char *)t->bt_cmap; ep = (u_char *)t->bt_emap; for (nrec = t->bt_nrecs; nrec < top; ++nrec) { if (sp >= ep) { F_SET(t, R_EOF); return (RET_SPECIAL); } len = t->bt_reclen; for (p = t->bt_rdata.data; sp < ep && len > 0; *p++ = *sp++, --len); if (len != 0) memset(p, t->bt_bval, len); if (__rec_iput(t, nrec, &data, 0) != RET_SUCCESS) return (RET_ERROR); } t->bt_cmap = (caddr_t)sp; return (RET_SUCCESS); } /* * __REC_VMAP -- Get variable length records from a file. * * Parameters: * t: tree * cnt: records to read * * Returns: * RET_ERROR, RET_SUCCESS */ int __rec_vmap(t, top) BTREE *t; recno_t top; { DBT data; u_char *sp, *ep; recno_t nrec; int bval; sp = (u_char *)t->bt_cmap; ep = (u_char *)t->bt_emap; bval = t->bt_bval; for (nrec = t->bt_nrecs; nrec < top; ++nrec) { if (sp >= ep) { F_SET(t, R_EOF); return (RET_SPECIAL); } for (data.data = sp; sp < ep && *sp != bval; ++sp); data.size = sp - (u_char *)data.data; if (__rec_iput(t, nrec, &data, 0) != RET_SUCCESS) return (RET_ERROR); ++sp; } t->bt_cmap = (caddr_t)sp; return (RET_SUCCESS); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)rec_open.c 8.12 (Berkeley) 11/18/94"; #endif /* LIBC_SCCS and not lint */ #include #ifdef RECNO_USE_MMAP #include #endif #include #include #include #include #include #include #include #include "db-int.h" #include "recno.h" DB * __rec_open(fname, flags, mode, openinfo, dflags) const char *fname; int flags, mode, dflags; const RECNOINFO *openinfo; { BTREE *t; BTREEINFO btopeninfo; DB *dbp; PAGE *h; struct stat sb; int rfd, sverrno; /* Open the user's file -- if this fails, we're done. */ if (fname != NULL && (rfd = open(fname, flags | O_BINARY, mode)) < 0) return (NULL); /* Create a btree in memory (backed by disk). */ dbp = NULL; if (openinfo) { if (openinfo->flags & ~(R_FIXEDLEN | R_NOKEY | R_SNAPSHOT)) goto einval; btopeninfo.flags = 0; btopeninfo.cachesize = openinfo->cachesize; btopeninfo.maxkeypage = 0; btopeninfo.minkeypage = 0; btopeninfo.psize = openinfo->psize; btopeninfo.compare = NULL; btopeninfo.prefix = NULL; btopeninfo.lorder = openinfo->lorder; dbp = __bt_open(openinfo->bfname, O_RDWR | O_BINARY, S_IRUSR | S_IWUSR, &btopeninfo, dflags); } else dbp = __bt_open(NULL, O_RDWR | O_BINARY, S_IRUSR | S_IWUSR, NULL, dflags); if (dbp == NULL) goto err; /* * Some fields in the tree structure are recno specific. Fill them * in and make the btree structure look like a recno structure. We * don't change the bt_ovflsize value, it's close enough and slightly * bigger. */ t = dbp->internal; if (openinfo) { if (openinfo->flags & R_FIXEDLEN) { F_SET(t, R_FIXLEN); t->bt_reclen = openinfo->reclen; if (t->bt_reclen == 0) goto einval; } t->bt_bval = openinfo->bval; } else t->bt_bval = '\n'; F_SET(t, R_RECNO); if (fname == NULL) F_SET(t, R_EOF | R_INMEM); else t->bt_rfd = rfd; if (fname != NULL) { /* * In 4.4BSD, stat(2) returns true for ISSOCK on pipes. * Unfortunately, that's not portable, so we use lseek * and check the errno values. */ errno = 0; if (lseek(rfd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) { switch (flags & O_ACCMODE) { case O_RDONLY: F_SET(t, R_RDONLY); break; default: goto einval; } slow: if ((t->bt_rfp = fdopen(rfd, "rb")) == NULL) goto err; F_SET(t, R_CLOSEFP); t->bt_irec = F_ISSET(t, R_FIXLEN) ? __rec_fpipe : __rec_vpipe; } else { switch (flags & O_ACCMODE) { case O_RDONLY: F_SET(t, R_RDONLY); break; case O_RDWR: break; default: goto einval; } if (fstat(rfd, &sb)) goto err; /* * Kluge -- we'd like to test to see if the file is too * big to mmap. Since, we don't know what size or type * off_t's or size_t's are, what the largest unsigned * integral type is, or what random insanity the local * C compiler will perpetrate, doing the comparison in * a portable way is flatly impossible. Hope that mmap * fails if the file is too large. */ if (sb.st_size == 0) F_SET(t, R_EOF); else { #ifdef RECNO_USE_MMAP /* * XXX * Mmap doesn't work correctly on many current * systems. In particular, it can fail subtly, * with cache coherency problems. Don't use it * for now. */ t->bt_msize = sb.st_size; if ((t->bt_smap = mmap(NULL, t->bt_msize, PROT_READ, MAP_PRIVATE, rfd, (off_t)0)) == (caddr_t)-1) goto slow; t->bt_cmap = t->bt_smap; t->bt_emap = t->bt_smap + sb.st_size; t->bt_irec = F_ISSET(t, R_FIXLEN) ? __rec_fmap : __rec_vmap; F_SET(t, R_MEMMAPPED); #else goto slow; #endif } } } /* Use the recno routines. */ dbp->close = __rec_close; dbp->del = __rec_delete; dbp->fd = __rec_fd; dbp->get = __rec_get; dbp->put = __rec_put; dbp->seq = __rec_seq; dbp->sync = __rec_sync; /* If the root page was created, reset the flags. */ if ((h = mpool_get(t->bt_mp, P_ROOT, 0)) == NULL) goto err; if ((h->flags & P_TYPE) == P_BLEAF) { F_CLR(h, P_TYPE); F_SET(h, P_RLEAF); mpool_put(t->bt_mp, h, MPOOL_DIRTY); } else mpool_put(t->bt_mp, h, 0); if (openinfo && openinfo->flags & R_SNAPSHOT && !F_ISSET(t, R_EOF | R_INMEM) && t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR) goto err; return (dbp); einval: errno = EINVAL; err: sverrno = errno; if (dbp != NULL) (void)__bt_close(dbp); if (fname != NULL) (void)close(rfd); errno = sverrno; return (NULL); } int __rec_fd(dbp) const DB *dbp; { BTREE *t; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } /* In-memory database can't have a file descriptor. */ if (F_ISSET(t, R_INMEM)) { errno = ENOENT; return (-1); } return (t->bt_rfd); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)rec_put.c 8.7 (Berkeley) 8/18/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include "db-int.h" #include "recno.h" /* * __REC_PUT -- Add a recno item to the tree. * * Parameters: * dbp: pointer to access method * key: key * data: data * flag: R_CURSOR, R_IAFTER, R_IBEFORE, R_NOOVERWRITE * * Returns: * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key is * already in the tree and R_NOOVERWRITE specified. */ int __rec_put(dbp, key, data, flags) const DB *dbp; DBT *key; const DBT *data; u_int flags; { BTREE *t; DBT fdata, tdata; recno_t nrec; int status; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } /* * If using fixed-length records, and the record is long, return * EINVAL. If it's short, pad it out. Use the record data return * memory, it's only short-term. */ if (F_ISSET(t, R_FIXLEN) && data->size != t->bt_reclen) { if (data->size > t->bt_reclen) goto einval; if (t->bt_rdata.size < t->bt_reclen) { t->bt_rdata.data = t->bt_rdata.data == NULL ? malloc(t->bt_reclen) : realloc(t->bt_rdata.data, t->bt_reclen); if (t->bt_rdata.data == NULL) return (RET_ERROR); t->bt_rdata.size = t->bt_reclen; } memmove(t->bt_rdata.data, data->data, data->size); memset((char *)t->bt_rdata.data + data->size, t->bt_bval, t->bt_reclen - data->size); fdata.data = t->bt_rdata.data; fdata.size = t->bt_reclen; } else { fdata.data = data->data; fdata.size = data->size; } switch (flags) { case R_CURSOR: if (!F_ISSET(&t->bt_cursor, CURS_INIT)) goto einval; nrec = t->bt_cursor.rcursor; break; case R_SETCURSOR: if ((nrec = *(recno_t *)key->data) == 0) goto einval; break; case R_IAFTER: if ((nrec = *(recno_t *)key->data) == 0) { nrec = 1; flags = R_IBEFORE; } break; case 0: case R_IBEFORE: if ((nrec = *(recno_t *)key->data) == 0) goto einval; break; case R_NOOVERWRITE: if ((nrec = *(recno_t *)key->data) == 0) goto einval; if (nrec <= t->bt_nrecs) return (RET_SPECIAL); break; default: einval: errno = EINVAL; return (RET_ERROR); } /* * Make sure that records up to and including the put record are * already in the database. If skipping records, create empty ones. */ if (nrec > t->bt_nrecs) { if (!F_ISSET(t, R_EOF | R_INMEM) && t->bt_irec(t, nrec) == RET_ERROR) return (RET_ERROR); if (nrec > t->bt_nrecs + 1) { if (F_ISSET(t, R_FIXLEN)) { if ((tdata.data = (void *)malloc(t->bt_reclen)) == NULL) return (RET_ERROR); tdata.size = t->bt_reclen; memset(tdata.data, t->bt_bval, tdata.size); } else { tdata.data = NULL; tdata.size = 0; } while (nrec > t->bt_nrecs + 1) if (__rec_iput(t, t->bt_nrecs, &tdata, 0) != RET_SUCCESS) return (RET_ERROR); if (F_ISSET(t, R_FIXLEN)) free(tdata.data); } } if ((status = __rec_iput(t, nrec - 1, &fdata, flags)) != RET_SUCCESS) return (status); if (flags == R_SETCURSOR) t->bt_cursor.rcursor = nrec; F_SET(t, R_MODIFIED); return (__rec_ret(t, NULL, nrec, key, NULL)); } /* * __REC_IPUT -- Add a recno item to the tree. * * Parameters: * t: tree * nrec: record number * data: data * * Returns: * RET_ERROR, RET_SUCCESS */ int __rec_iput(t, nrec, data, flags) BTREE *t; recno_t nrec; const DBT *data; u_int flags; { DBT tdata; EPG *e; PAGE *h; indx_t idx, nxtindex; db_pgno_t pg; u_int32_t nbytes; int dflags, status; char *dest, db[NOVFLSIZE]; /* * If the data won't fit on a page, store it on indirect pages. * * XXX * If the insert fails later on, these pages aren't recovered. */ if (data->size > t->bt_ovflsize) { if (__ovfl_put(t, data, &pg) == RET_ERROR) return (RET_ERROR); tdata.data = db; tdata.size = NOVFLSIZE; *(db_pgno_t *)db = pg; *(u_int32_t *)(db + sizeof(db_pgno_t)) = data->size; dflags = P_BIGDATA; data = &tdata; } else dflags = 0; /* __rec_search pins the returned page. */ if ((e = __rec_search(t, nrec, nrec > t->bt_nrecs || flags == R_IAFTER || flags == R_IBEFORE ? SINSERT : SEARCH)) == NULL) return (RET_ERROR); h = e->page; idx = e->index; /* * Add the specified key/data pair to the tree. The R_IAFTER and * R_IBEFORE flags insert the key after/before the specified key. * * Pages are split as required. */ switch (flags) { case R_IAFTER: ++idx; break; case R_IBEFORE: break; default: if (nrec < t->bt_nrecs && __rec_dleaf(t, h, idx) == RET_ERROR) { mpool_put(t->bt_mp, h, 0); return (RET_ERROR); } break; } /* * If not enough room, split the page. The split code will insert * the key and data and unpin the current page. If inserting into * the offset array, shift the pointers up. */ nbytes = NRLEAFDBT(data->size); if (h->upper - h->lower < nbytes + sizeof(indx_t)) { status = __bt_split(t, h, NULL, data, dflags, nbytes, idx); if (status == RET_SUCCESS) ++t->bt_nrecs; return (status); } if (idx < (nxtindex = NEXTINDEX(h))) memmove(h->linp + idx + 1, h->linp + idx, (nxtindex - idx) * sizeof(indx_t)); h->lower += sizeof(indx_t); h->linp[idx] = h->upper -= nbytes; dest = (char *)h + h->upper; WR_RLEAF(dest, data, dflags); ++t->bt_nrecs; F_SET(t, B_MODIFIED); mpool_put(t->bt_mp, h, MPOOL_DIRTY); return (RET_SUCCESS); } /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)rec_search.c 8.4 (Berkeley) 7/14/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include "db-int.h" #include "recno.h" /* * __REC_SEARCH -- Search a btree for a key. * * Parameters: * t: tree to search * recno: key to find * op: search operation * * Returns: * EPG for matching record, if any, or the EPG for the location of the * key, if it were inserted into the tree. * * Returns: * The EPG for matching record, if any, or the EPG for the location * of the key, if it were inserted into the tree, is entered into * the bt_cur field of the tree. A pointer to the field is returned. */ EPG * __rec_search(t, recno, op) BTREE *t; recno_t recno; enum SRCHOP op; { register indx_t idx; register PAGE *h; EPGNO *parent; RINTERNAL *r; db_pgno_t pg; indx_t top; recno_t total; int sverrno; BT_CLR(t); for (pg = P_ROOT, total = 0;;) { if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) goto err; if (h->flags & P_RLEAF) { t->bt_cur.page = h; t->bt_cur.index = recno - total; return (&t->bt_cur); } for (idx = 0, top = NEXTINDEX(h);;) { r = GETRINTERNAL(h, idx); if (++idx == top || total + r->nrecs > recno) break; total += r->nrecs; } BT_PUSH(t, pg, idx - 1); pg = r->pgno; switch (op) { case SDELETE: --GETRINTERNAL(h, (idx - 1))->nrecs; mpool_put(t->bt_mp, h, MPOOL_DIRTY); break; case SINSERT: ++GETRINTERNAL(h, (idx - 1))->nrecs; mpool_put(t->bt_mp, h, MPOOL_DIRTY); break; case SEARCH: mpool_put(t->bt_mp, h, 0); break; } } /* Try and recover the tree. */ err: sverrno = errno; if (op != SEARCH) while ((parent = BT_POP(t)) != NULL) { if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) break; if (op == SINSERT) --GETRINTERNAL(h, parent->index)->nrecs; else ++GETRINTERNAL(h, parent->index)->nrecs; mpool_put(t->bt_mp, h, MPOOL_DIRTY); } errno = sverrno; return (NULL); } /*- * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)rec_seq.c 8.3 (Berkeley) 7/14/94"; #endif /* not lint */ #include #include #include #include #include #include "db-int.h" #include "recno.h" /* * __REC_SEQ -- Recno sequential scan interface. * * Parameters: * dbp: pointer to access method * key: key for positioning and return value * data: data return value * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV. * * Returns: * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. */ int __rec_seq(dbp, key, data, flags) const DB *dbp; DBT *key, *data; u_int flags; { BTREE *t; EPG *e; recno_t nrec; int status; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } switch(flags) { case R_CURSOR: if ((nrec = *(recno_t *)key->data) == 0) goto einval; break; case R_NEXT: if (F_ISSET(&t->bt_cursor, CURS_INIT)) { nrec = t->bt_cursor.rcursor + 1; break; } /* FALLTHROUGH */ case R_FIRST: nrec = 1; break; case R_PREV: if (F_ISSET(&t->bt_cursor, CURS_INIT)) { if ((nrec = t->bt_cursor.rcursor - 1) == 0) return (RET_SPECIAL); break; } /* FALLTHROUGH */ case R_LAST: if (!F_ISSET(t, R_EOF | R_INMEM) && t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR) return (RET_ERROR); nrec = t->bt_nrecs; break; default: einval: errno = EINVAL; return (RET_ERROR); } if (t->bt_nrecs == 0 || nrec > t->bt_nrecs) { if (!F_ISSET(t, R_EOF | R_INMEM) && (status = t->bt_irec(t, nrec)) != RET_SUCCESS) return (status); if (t->bt_nrecs == 0 || nrec > t->bt_nrecs) return (RET_SPECIAL); } if ((e = __rec_search(t, nrec - 1, SEARCH)) == NULL) return (RET_ERROR); F_SET(&t->bt_cursor, CURS_INIT); t->bt_cursor.rcursor = nrec; status = __rec_ret(t, e, nrec, key, data); if (F_ISSET(t, B_DB_LOCK)) mpool_put(t->bt_mp, e->page, 0); else t->bt_pinned = e->page; return (status); } /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)rec_utils.c 8.6 (Berkeley) 7/16/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include "db-int.h" #include "recno.h" /* * __rec_ret -- * Build return data. * * Parameters: * t: tree * e: key/data pair to be returned * nrec: record number * key: user's key structure * data: user's data structure * * Returns: * RET_SUCCESS, RET_ERROR. */ int __rec_ret(t, e, nrec, key, data) BTREE *t; EPG *e; recno_t nrec; DBT *key, *data; { RLEAF *rl; void *p; if (key == NULL) goto dataonly; /* We have to copy the key, it's not on the page. */ if (sizeof(recno_t) > t->bt_rkey.size) { p = (void *)(t->bt_rkey.data == NULL ? malloc(sizeof(recno_t)) : realloc(t->bt_rkey.data, sizeof(recno_t))); if (p == NULL) return (RET_ERROR); t->bt_rkey.data = p; t->bt_rkey.size = sizeof(recno_t); } memmove(t->bt_rkey.data, &nrec, sizeof(recno_t)); key->size = sizeof(recno_t); key->data = t->bt_rkey.data; dataonly: if (data == NULL) return (RET_SUCCESS); /* * We must copy big keys/data to make them contigous. Otherwise, * leave the page pinned and don't copy unless the user specified * concurrent access. */ rl = GETRLEAF(e->page, e->index); if (rl->flags & P_BIGDATA) { if (__ovfl_get(t, rl->bytes, &data->size, &t->bt_rdata.data, &t->bt_rdata.size)) return (RET_ERROR); data->data = t->bt_rdata.data; } else if (F_ISSET(t, B_DB_LOCK)) { /* Use +1 in case the first record retrieved is 0 length. */ if (rl->dsize + 1 > t->bt_rdata.size) { p = (void *)(t->bt_rdata.data == NULL ? malloc(rl->dsize + 1) : realloc(t->bt_rdata.data, rl->dsize + 1)); if (p == NULL) return (RET_ERROR); t->bt_rdata.data = p; t->bt_rdata.size = rl->dsize + 1; } memmove(t->bt_rdata.data, rl->bytes, rl->dsize); data->size = rl->dsize; data->data = t->bt_rdata.data; } else { data->size = rl->dsize; data->data = rl->bytes; } return (RET_SUCCESS); } /* * Copyright (c) 1997-2000 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _KRB5_DB2_RECNO_RECNO_H #define _KRB5_DB2_RECNO_RECNO_H #ifdef __cplusplus extern "C" { #endif /* * Copyright (c) 1991, 1993 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)recno.h 8.1 (Berkeley) 6/4/93 */ enum SRCHOP { SDELETE, SINSERT, SEARCH}; /* Rec_search operation. */ #include "../btree/btree.h" #include "extern.h" #ifdef __cplusplus } #endif #endif /* !_KRB5_DB2_RECNO_RECNO_H */ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION SUNWprivate_1.1 { global: kdb_function_table { ASSERT = { TYPE = OBJECT; SIZE = addrsize[39]; }; }; local: *; }; #include /* Solaris Kerberos: gssrpc not supported */ #if 0 /************** Begin IFDEF'ed OUT *******************************/ #include #else #include #include #endif /**************** END IFDEF'ed OUT *******************************/ #include #include "policy_db.h" #ifdef HAVE_MEMORY_H #include #endif #include #include /* Solaris Kerberos: this function taken from MIT's src/lib/rpc/xdr.c */ bool_t xdr_u_int32(XDR *xdrs, uint32_t *up) { u_long ul; switch (xdrs->x_op) { case XDR_ENCODE: ul = *up; return (xdr_u_long(xdrs, &ul)); case XDR_DECODE: if (!xdr_u_long(xdrs, &ul)) { return (FALSE); } *up = ul; return (TRUE); case XDR_FREE: return (TRUE); } return (FALSE); } static bool_t xdr_nullstring(XDR *xdrs, char **objp) { u_int size; if (xdrs->x_op == XDR_ENCODE) { if (*objp == NULL) size = 0; else size = strlen(*objp) + 1; } if (! xdr_u_int(xdrs, &size)) { return FALSE; } switch (xdrs->x_op) { case XDR_DECODE: if (size == 0) { *objp = NULL; return TRUE; } else if (*objp == NULL) { *objp = (char *) mem_alloc(size); if (*objp == NULL) { errno = ENOMEM; return FALSE; } } return (xdr_opaque(xdrs, *objp, size)); case XDR_ENCODE: if (size != 0) return (xdr_opaque(xdrs, *objp, size)); return TRUE; case XDR_FREE: if (*objp != NULL) mem_free(*objp, size); *objp = NULL; return TRUE; } return FALSE; } bool_t xdr_osa_policy_ent_rec(XDR *xdrs, osa_policy_ent_t objp) { switch (xdrs->x_op) { case XDR_ENCODE: objp->version = OSA_ADB_POLICY_VERSION_1; /* fall through */ case XDR_FREE: if (!xdr_int(xdrs, &objp->version)) return FALSE; break; case XDR_DECODE: if (!xdr_int(xdrs, &objp->version)) return FALSE; if (objp->version != OSA_ADB_POLICY_VERSION_1) return FALSE; break; } if(!xdr_nullstring(xdrs, &objp->name)) return (FALSE); if (!xdr_u_int32(xdrs, &objp->pw_min_life)) return (FALSE); if (!xdr_u_int32(xdrs, &objp->pw_max_life)) return (FALSE); if (!xdr_u_int32(xdrs, &objp->pw_min_length)) return (FALSE); if (!xdr_u_int32(xdrs, &objp->pw_min_classes)) return (FALSE); if (!xdr_u_int32(xdrs, &objp->pw_history_num)) return (FALSE); if (!xdr_u_int32(xdrs, &objp->policy_refcnt)) return (FALSE); return (TRUE); } /* * Data Types for policy and principal information that * exists in the respective databases. * * $Header$ * * This file was originally created with rpcgen. * It has been hacked up since then. */ #ifndef __ADB_H__ #define __ADB_H__ #include #include #include #include /* Okay, this is a bit obscure. The libdb2 configure script doesn't detect it, but on Tru64 5.1, netinet/in.h causes sys/bittypes.h to be included, and that has a typedef for u_int32_t. Because the configure script doesn't detect it, it causes db-config.h to have a #define for u_int32_t, so including db.h and then netinet/in.h causes compilation to fail. Since gssrpc/types.h includes netinet/in.h, including that first will cause the typedef to be seen before the macro definition, which still isn't quite right, but is close enough for now. A better fix might be for db.h to include netinet/in.h if that's where we find u_int32_t. */ /* Solaris Kerberos: gssrpc not supported */ #if 0 /************** Begin IFDEF'ed OUT *******************************/ #include #include #else #include #include #endif /**************** END IFDEF'ed OUT *******************************/ #include #include #include typedef long osa_adb_ret_t; #define OSA_ADB_POLICY_DB_MAGIC 0x12345A00 #define OSA_ADB_POLICY_VERSION_MASK 0x12345D00 #define OSA_ADB_POLICY_VERSION_1 0x12345D01 typedef struct _osa_adb_db_lock_ent_t { FILE *lockfile; char *filename; int refcnt, lockmode, lockcnt; krb5_context context; } osa_adb_lock_ent, *osa_adb_lock_t; typedef struct _osa_adb_db_ent_t { int magic; DB *db; HASHINFO info; BTREEINFO btinfo; char *filename; osa_adb_lock_t lock; int opencnt; } osa_adb_db_ent, *osa_adb_db_t, *osa_adb_princ_t, *osa_adb_policy_t; /* * Return Code (the rest are in adb_err.h) */ #define OSA_ADB_OK 0 /* * Functions */ krb5_error_code osa_adb_create_db(char *filename, char *lockfile, int magic); krb5_error_code osa_adb_destroy_db(char *filename, char *lockfile, int magic); krb5_error_code osa_adb_rename_db(char *filefrom, char *lockfrom, char *fileto, char *lockto, int magic); krb5_error_code osa_adb_init_db(osa_adb_db_t *dbp, char *filename, char *lockfile, int magic); krb5_error_code osa_adb_fini_db(osa_adb_db_t db, int magic); krb5_error_code osa_adb_get_lock(osa_adb_db_t db, int mode); krb5_error_code osa_adb_release_lock(osa_adb_db_t db); krb5_error_code osa_adb_open_and_lock(osa_adb_princ_t db, int locktype); krb5_error_code osa_adb_close_and_unlock(osa_adb_princ_t db); krb5_error_code osa_adb_close_policy(osa_adb_policy_t db); krb5_error_code osa_adb_create_policy(osa_adb_policy_t db, osa_policy_ent_t entry); krb5_error_code osa_adb_destroy_policy(osa_adb_policy_t db, char * name); krb5_error_code osa_adb_get_policy(osa_adb_policy_t db, char * name, osa_policy_ent_t *entry, int *cnt); krb5_error_code osa_adb_put_policy(osa_adb_policy_t db, osa_policy_ent_t entry); krb5_error_code osa_adb_iter_policy(osa_adb_policy_t db, osa_adb_iter_policy_func func, void * data); void osa_free_policy_ent(osa_policy_ent_t val); bool_t xdr_osa_policy_ent_rec(XDR *xdrs, osa_policy_ent_t objp); #endif /* __ADB_H__ */ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # include global definitions include ../../../../Makefile.master # # Build everything in parallel; use .WAIT for dependencies .PARALLEL: SUBDIRS= \ pkinit all : TARGET= all install : TARGET= install clean : TARGET= clean clobber : TARGET= clobber _msg : TARGET= _msg .KEEP_STATE: all install clean clobber _msg: $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # include $(SRC)/lib/krb5/Makefile.lib include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 # Hammerhead: amd64-only SUBDIRS= $(MACH64) all : TARGET= all clean : TARGET= clean clobber : TARGET= clobber install : TARGET= install _msg : TARGET= _msg .KEEP_STATE: all clean clobber _msg: $(SUBDIRS) $(ROOTLIBDIR): $(INS.dir) $(ROOTLIBDIR)/%: % $(INS.file) install: $(ROOTLIBDIR) $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. LIBRARY= pkinit.a VERS= .1 PKINIT_OBJS= \ pkinit_accessor.o \ pkinit_clnt.o \ pkinit_crypto_openssl.o \ pkinit_identity.o \ pkinit_lib.o \ pkinit_matching.o \ pkinit_profile.o \ pkinit_srv.o OBJECTS= $(PKINIT_OBJS) # include library definitions include $(SRC)/lib/krb5/Makefile.lib SRCS= $(PKINIT_OBJS:%.o=../%.c) LIBS= $(DYNLIB) include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 POFILE = $(LIBRARY:%.a=%.po) POFILES = generic.po #override liblink INS.liblink= -$(RM) $@; $(SYMLINK) $(LIBLINKS)$(VERS) $@ CPPFLAGS += -I$(SRC)/lib/krb5 \ -I$(SRC)/lib/krb5/kdb \ -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/lib/gss_mechs/mech_krb5/krb5/os \ -I$(SRC)/lib/gss_mechs/mech_krb5/include/krb5 \ -I$(SRC)/uts/common/gssapi/include/ \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \ -I$(SRC) # The pkinit_crypto_openssl.c file is not ported to the OpenSSL 3.x API yet and # so we need to make sure the deprecated 1.x API is fully available for it. # Once the pkinit_crypto_openssl.c file is adapted for OpenSSL 3.x this should # be updated or removed. # See also https://www.illumos.org/issues/16915 CPPFLAGS += -DOPENSSL_API_COMPAT=10101 CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-unused-function CERRWARN += -Wno-pointer-to-int-cast # not linted SMATCH=off # Hammerhead: -I.. must be in CPPFLAGS (not CFLAGS) to survive Makefile.master.64 reset CFLAGS += $(CCVERBOSE) CPPFLAGS += -I.. DYNFLAGS += $(KRUNPATH) $(KMECHLIB) $(ZNODELETE) LDLIBS += -L $(ROOTLIBDIR) -lcrypto -lc ROOTLIBDIR= $(ROOT)/usr/lib/krb5/plugins/preauth $(ROOTLIBDIR): $(INS.dir) .KEEP_STATE: all: $(LIBS) # include library targets include $(SRC)/lib/krb5/Makefile.targ FRC: generic.po: FRC $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext ../*.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2025 Hammerhead Project # include ../Makefile.com include $(SRC)/lib/Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. # # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # # Due to mistakes made early in the history of this library, there are # no SUNW_1.1 symbols, but the version is now kept as a placeholder. # Don't add any symbols to this version. $mapfile_version 2 SYMBOL_VERSION SUNWprivate_1.1 { global: preauthentication_client_1 { ASSERT = { TYPE = OBJECT; SIZE = addrsize[11]; }; }; preauthentication_server_1 { ASSERT = { TYPE = OBJECT; SIZE = addrsize[9]; }; }; local: *; }; /* * COPYRIGHT (C) 2006,2007 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN * ALL RIGHTS RESERVED * * Permission is granted to use, copy, create derivative works * and redistribute this software and such derivative works * for any purpose, so long as the name of The University of * Michigan is not used in any advertising or publicity * pertaining to the use of distribution of this software * without specific, written prior authorization. If the * above copyright notice or any other identification of the * University of Michigan is included in any copy of any * portion of this software, then the disclaimer below must * also be included. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF * SUCH DAMAGES. */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _PKINIT_H #define _PKINIT_H /* Solaris Kerberos */ #include #include #include #include "pkinit_accessor.h" /* * It is anticipated that all the special checks currently * required when talking to a Longhorn server will go away * by the time it is officially released and all references * to the longhorn global can be removed and any code * #ifdef'd with LONGHORN_BETA_COMPAT can be removed. * And this #define! */ #define LONGHORN_BETA_COMPAT 1 #ifdef LONGHORN_BETA_COMPAT extern int longhorn; /* XXX Talking to a Longhorn server? */ #endif #ifndef WITHOUT_PKCS11 /* Solaris Kerberos */ #include #include /* Solaris Kerberos */ #define PKCS11_MODNAME "/usr/lib/libpkcs11.so" #define PK_SIGLEN_GUESS 1000 #define PK_NOSLOT 999999 #endif #define DH_PROTOCOL 1 #define RSA_PROTOCOL 2 #define TD_TRUSTED_CERTIFIERS 104 #define TD_INVALID_CERTIFICATES 105 #define TD_DH_PARAMETERS 109 #define PKINIT_CTX_MAGIC 0x05551212 #define PKINIT_REQ_CTX_MAGIC 0xdeadbeef #define PKINIT_DEFAULT_DH_MIN_BITS 2048 /* Make pkiDebug(fmt,...) print, or not. */ #ifdef DEBUG #define pkiDebug printf #else /* Still evaluates for side effects. */ /* ARGSUSED */ static void pkiDebug (const char *fmt, ...) { } /* This is better if the compiler doesn't inline variadic functions well, but gcc will warn about "left-hand operand of comma expression has no effect". Still evaluates for side effects. */ /* #define pkiDebug (void) */ #endif /* Solaris Kerberos */ #if (__STDC_VERSION__ >= 199901L) || \ (defined(__SUNPRO_C) && defined(__C99FEATURES__)) #define __FUNCTION__ __func__ #else #define __FUNCTION__ "" #endif /* Macros to deal with converting between various data types... */ #define PADATA_TO_KRB5DATA(pad, k5d) \ (k5d)->length = (pad)->length; (k5d)->data = (char *)(pad)->contents; #define OCTETDATA_TO_KRB5DATA(octd, k5d) \ (k5d)->length = (octd)->length; (k5d)->data = (char *)(octd)->data; extern const krb5_octet_data dh_oid; /* * notes about crypto contexts: * * the basic idea is that there are crypto contexts that live at * both the plugin level and request level. the identity context (that * keeps info about your own certs and such) is separate because * it is needed at different levels for the kdc and and the client. * (the kdc's identity is at the plugin level, the client's identity * information could change per-request.) * the identity context is meant to have the entity's cert, * a list of trusted and intermediate cas, a list of crls, and any * pkcs11 information. the req context is meant to have the * received certificate and the DH related information. the plugin * context is meant to have global crypto information, i.e., OIDs * and constant DH parameter information. */ /* * plugin crypto context should keep plugin common information, * eg., OIDs, known DHparams */ typedef struct _pkinit_plg_crypto_context *pkinit_plg_crypto_context; /* * request crypto context should keep reqyest common information, * eg., received credentials, DH parameters of this request */ typedef struct _pkinit_req_crypto_context *pkinit_req_crypto_context; /* * identity context should keep information about credentials * for the request, eg., my credentials, trusted ca certs, * intermediate ca certs, crls, pkcs11 info */ typedef struct _pkinit_identity_crypto_context *pkinit_identity_crypto_context; /* * this structure keeps information about the config options */ typedef struct _pkinit_plg_opts { int require_eku; /* require EKU checking (default is true) */ int accept_secondary_eku;/* accept secondary EKU (default is false) */ int allow_upn; /* allow UPN-SAN instead of pkinit-SAN */ int dh_or_rsa; /* selects DH or RSA based pkinit */ int require_crl_checking; /* require CRL for a CA (default is false) */ int dh_min_bits; /* minimum DH modulus size allowed */ } pkinit_plg_opts; /* * this structure keeps options used for a given request */ typedef struct _pkinit_req_opts { int require_eku; int accept_secondary_eku; int allow_upn; int dh_or_rsa; int require_crl_checking; int dh_size; /* initial request DH modulus size (default=1024) */ int require_hostname_match; int win2k_target; int win2k_require_cksum; } pkinit_req_opts; /* * information about identity from config file or command line */ #define PKINIT_ID_OPT_USER_IDENTITY 1 #define PKINIT_ID_OPT_ANCHOR_CAS 2 #define PKINIT_ID_OPT_INTERMEDIATE_CAS 3 #define PKINIT_ID_OPT_CRLS 4 #define PKINIT_ID_OPT_OCSP 5 #define PKINIT_ID_OPT_DN_MAPPING 6 /* XXX ? */ typedef struct _pkinit_identity_opts { char *identity; char **identity_alt; char **anchors; char **intermediates; char **crls; char *ocsp; char *dn_mapping_file; int idtype; char *cert_filename; char *key_filename; #ifndef WITHOUT_PKCS11 char *p11_module_name; CK_SLOT_ID slotid; char *token_label; char *cert_id_string; char *cert_label; char *PIN; /* Solaris Kerberos */ #endif } pkinit_identity_opts; /* * Client's plugin context */ struct _pkinit_context { int magic; pkinit_plg_crypto_context cryptoctx; pkinit_plg_opts *opts; pkinit_identity_opts *idopts; }; typedef struct _pkinit_context *pkinit_context; /* * Client's per-request context */ struct _pkinit_req_context { int magic; pkinit_req_crypto_context cryptoctx; pkinit_req_opts *opts; pkinit_identity_crypto_context idctx; pkinit_identity_opts *idopts; krb5_preauthtype pa_type; }; typedef struct _pkinit_kdc_context *pkinit_kdc_context; /* * KDC's (per-realm) plugin context */ struct _pkinit_kdc_context { int magic; pkinit_plg_crypto_context cryptoctx; pkinit_plg_opts *opts; pkinit_identity_crypto_context idctx; pkinit_identity_opts *idopts; char *realmname; unsigned int realmname_len; }; typedef struct _pkinit_req_context *pkinit_req_context; /* * KDC's per-request context */ struct _pkinit_kdc_req_context { int magic; pkinit_req_crypto_context cryptoctx; krb5_auth_pack *rcv_auth_pack; krb5_auth_pack_draft9 *rcv_auth_pack9; krb5_preauthtype pa_type; }; typedef struct _pkinit_kdc_req_context *pkinit_kdc_req_context; /* * Functions in pkinit_lib.c */ krb5_error_code pkinit_init_req_opts(pkinit_req_opts **); void pkinit_fini_req_opts(pkinit_req_opts *); krb5_error_code pkinit_init_plg_opts(pkinit_plg_opts **); void pkinit_fini_plg_opts(pkinit_plg_opts *); krb5_error_code pkinit_init_identity_opts(pkinit_identity_opts **idopts); void pkinit_fini_identity_opts(pkinit_identity_opts *idopts); krb5_error_code pkinit_dup_identity_opts(pkinit_identity_opts *src_opts, pkinit_identity_opts **dest_opts); /* * Functions in pkinit_identity.c */ char * idtype2string(int idtype); char * catype2string(int catype); krb5_error_code pkinit_identity_initialize (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_opts *idopts, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN/OUT */ int do_matching, /* IN */ krb5_principal princ); /* IN (optional) */ krb5_error_code pkinit_cert_matching (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_principal princ, krb5_boolean do_select); /* * initialization and free functions */ void init_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in); void init_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in); void init_krb5_reply_key_pack(krb5_reply_key_pack **in); void init_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in); void init_krb5_auth_pack(krb5_auth_pack **in); void init_krb5_auth_pack_draft9(krb5_auth_pack_draft9 **in); void init_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in); void init_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in); void init_krb5_typed_data(krb5_typed_data **in); void init_krb5_subject_pk_info(krb5_subject_pk_info **in); void free_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in); void free_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in); void free_krb5_reply_key_pack(krb5_reply_key_pack **in); void free_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in); void free_krb5_auth_pack(krb5_auth_pack **in); void free_krb5_auth_pack_draft9(krb5_context, krb5_auth_pack_draft9 **in); void free_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in); void free_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in); void free_krb5_external_principal_identifier(krb5_external_principal_identifier ***in); void free_krb5_trusted_ca(krb5_trusted_ca ***in); void free_krb5_typed_data(krb5_typed_data ***in); void free_krb5_algorithm_identifiers(krb5_algorithm_identifier ***in); void free_krb5_algorithm_identifier(krb5_algorithm_identifier *in); void free_krb5_kdc_dh_key_info(krb5_kdc_dh_key_info **in); void free_krb5_subject_pk_info(krb5_subject_pk_info **in); krb5_error_code pkinit_copy_krb5_octet_data(krb5_octet_data *dst, const krb5_octet_data *src); /* * Functions in pkinit_profile.c */ krb5_error_code pkinit_kdcdefault_strings (krb5_context context, const char *realmname, const char *option, char ***ret_value); krb5_error_code pkinit_kdcdefault_string (krb5_context context, const char *realmname, const char *option, char **ret_value); krb5_error_code pkinit_kdcdefault_boolean (krb5_context context, const char *realmname, const char *option, int default_value, int *ret_value); krb5_error_code pkinit_kdcdefault_integer (krb5_context context, const char *realmname, const char *option, int default_value, int *ret_value); krb5_error_code pkinit_libdefault_strings (krb5_context context, const krb5_data *realm, const char *option, char ***ret_value); krb5_error_code pkinit_libdefault_string (krb5_context context, const krb5_data *realm, const char *option, char **ret_value); krb5_error_code pkinit_libdefault_boolean (krb5_context context, const krb5_data *realm, const char *option, int default_value, int *ret_value); krb5_error_code pkinit_libdefault_integer (krb5_context context, const krb5_data *realm, const char *option, int default_value, int *ret_value); /* * debugging functions */ void print_buffer(unsigned char *, unsigned int); void print_buffer_bin(unsigned char *, unsigned int, char *); /* * Now get crypto function declarations */ #include "pkinit_crypto.h" #endif /* _PKINIT_H */ /* * COPYRIGHT (C) 2006,2007 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN * ALL RIGHTS RESERVED * * Permission is granted to use, copy, create derivative works * and redistribute this software and such derivative works * for any purpose, so long as the name of The University of * Michigan is not used in any advertising or publicity * pertaining to the use of distribution of this software * without specific, written prior authorization. If the * above copyright notice or any other identification of the * University of Michigan is included in any copy of any * portion of this software, then the disclaimer below must * also be included. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF * SUCH DAMAGES. */ #include #include "pkinit_accessor.h" #define DEF_FUNC_PTRS(type) \ krb5_error_code (*k5int_encode_##type)(const type *, krb5_data **); \ krb5_error_code (*k5int_decode_##type)(const krb5_data *, type **) #define DEF_FUNC_PTRS_ARRAY(type) \ krb5_error_code (*k5int_encode_##type)(const type **, krb5_data **); \ krb5_error_code (*k5int_decode_##type)(const krb5_data *, type ***) DEF_FUNC_PTRS(krb5_auth_pack); DEF_FUNC_PTRS(krb5_auth_pack_draft9); DEF_FUNC_PTRS(krb5_kdc_dh_key_info); DEF_FUNC_PTRS(krb5_pa_pk_as_rep); DEF_FUNC_PTRS(krb5_pa_pk_as_rep_draft9); DEF_FUNC_PTRS(krb5_pa_pk_as_req); DEF_FUNC_PTRS(krb5_pa_pk_as_req_draft9); DEF_FUNC_PTRS(krb5_reply_key_pack); DEF_FUNC_PTRS(krb5_reply_key_pack_draft9); DEF_FUNC_PTRS_ARRAY(krb5_typed_data); /* special cases... */ krb5_error_code (*k5int_decode_krb5_principal_name) (const krb5_data *, krb5_principal_data **); krb5_error_code (*k5int_encode_krb5_td_dh_parameters) (const krb5_algorithm_identifier **, krb5_data **code); krb5_error_code (*k5int_decode_krb5_td_dh_parameters) (const krb5_data *, krb5_algorithm_identifier ***); krb5_error_code (*k5int_encode_krb5_td_trusted_certifiers) (const krb5_external_principal_identifier **, krb5_data **code); krb5_error_code (*k5int_decode_krb5_td_trusted_certifiers) (const krb5_data *, krb5_external_principal_identifier ***); krb5_error_code (*k5int_decode_krb5_as_req) (const krb5_data *output, krb5_kdc_req **rep); krb5_error_code (*k5int_encode_krb5_kdc_req_body) (const krb5_kdc_req *rep, krb5_data **code); void KRB5_CALLCONV (*k5int_krb5_free_kdc_req) (krb5_context, krb5_kdc_req * ); void (*k5int_set_prompt_types) (krb5_context, krb5_prompt_type *); krb5_error_code (*k5int_encode_krb5_authdata_elt) (const krb5_authdata *rep, krb5_data **code); /* * Grab internal function pointers from the krb5int_accessor * structure and make them available */ krb5_error_code pkinit_accessor_init(void) { krb5_error_code retval; krb5int_access k5int; retval = krb5int_accessor(&k5int, KRB5INT_ACCESS_VERSION); if (retval) return retval; #define SET_PTRS(type) \ k5int_encode_##type = k5int.encode_##type; \ k5int_decode_##type = k5int.decode_##type; SET_PTRS(krb5_auth_pack); SET_PTRS(krb5_auth_pack_draft9); SET_PTRS(krb5_kdc_dh_key_info); SET_PTRS(krb5_pa_pk_as_rep); SET_PTRS(krb5_pa_pk_as_rep_draft9); SET_PTRS(krb5_pa_pk_as_req); SET_PTRS(krb5_pa_pk_as_req_draft9); SET_PTRS(krb5_reply_key_pack); SET_PTRS(krb5_reply_key_pack_draft9); SET_PTRS(krb5_td_dh_parameters); SET_PTRS(krb5_td_trusted_certifiers); SET_PTRS(krb5_typed_data); /* special cases... */ k5int_decode_krb5_principal_name = k5int.decode_krb5_principal_name; k5int_decode_krb5_as_req = k5int.decode_krb5_as_req; k5int_encode_krb5_kdc_req_body = k5int.encode_krb5_kdc_req_body; k5int_krb5_free_kdc_req = k5int.krb5_free_kdc_req; k5int_set_prompt_types = k5int.krb5int_set_prompt_types; k5int_encode_krb5_authdata_elt = k5int.encode_krb5_authdata_elt; return 0; } /* * COPYRIGHT (C) 2006,2007 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN * ALL RIGHTS RESERVED * * Permission is granted to use, copy, create derivative works * and redistribute this software and such derivative works * for any purpose, so long as the name of The University of * Michigan is not used in any advertising or publicity * pertaining to the use of distribution of this software * without specific, written prior authorization. If the * above copyright notice or any other identification of the * University of Michigan is included in any copy of any * portion of this software, then the disclaimer below must * also be included. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF * SUCH DAMAGES. */ #ifndef _PKINIT_ACCESSOR_H #define _PKINIT_ACCESSOR_H /* * Function prototypes */ krb5_error_code pkinit_accessor_init(void); #define DEF_EXT_FUNC_PTRS(type) \ extern krb5_error_code (*k5int_encode_##type)(const type *, krb5_data **); \ extern krb5_error_code (*k5int_decode_##type)(const krb5_data *, type **) #define DEF_EXT_FUNC_PTRS_ARRAY(type) \ extern krb5_error_code (*k5int_encode_##type)(const type **, krb5_data **); \ extern krb5_error_code (*k5int_decode_##type)(const krb5_data *, type ***) DEF_EXT_FUNC_PTRS(krb5_auth_pack); DEF_EXT_FUNC_PTRS(krb5_auth_pack_draft9); DEF_EXT_FUNC_PTRS(krb5_kdc_dh_key_info); DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_rep); DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_rep_draft9); DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_req); DEF_EXT_FUNC_PTRS(krb5_pa_pk_as_req_draft9); DEF_EXT_FUNC_PTRS(krb5_reply_key_pack); DEF_EXT_FUNC_PTRS(krb5_reply_key_pack_draft9); DEF_EXT_FUNC_PTRS_ARRAY(krb5_typed_data); /* special cases... */ extern krb5_error_code (*k5int_decode_krb5_principal_name) (const krb5_data *, krb5_principal_data **); extern krb5_error_code (*k5int_encode_krb5_td_dh_parameters) (const krb5_algorithm_identifier **, krb5_data **code); extern krb5_error_code (*k5int_decode_krb5_td_dh_parameters) (const krb5_data *, krb5_algorithm_identifier ***); extern krb5_error_code (*k5int_encode_krb5_td_trusted_certifiers) (const krb5_external_principal_identifier **, krb5_data **code); extern krb5_error_code (*k5int_decode_krb5_td_trusted_certifiers) (const krb5_data *, krb5_external_principal_identifier ***); extern krb5_error_code (*k5int_decode_krb5_as_req) (const krb5_data *output, krb5_kdc_req **rep); extern krb5_error_code (*k5int_encode_krb5_kdc_req_body) (const krb5_kdc_req *rep, krb5_data **code); extern void KRB5_CALLCONV (*k5int_krb5_free_kdc_req) (krb5_context, krb5_kdc_req * ); extern void (*k5int_set_prompt_types) (krb5_context, krb5_prompt_type *); extern krb5_error_code (*k5int_encode_krb5_authdata_elt) (const krb5_authdata *rep, krb5_data **code); #endif /* _PKINIT_ACCESSOR_H */ /* * COPYRIGHT (C) 2006,2007 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN * ALL RIGHTS RESERVED * * Permission is granted to use, copy, create derivative works * and redistribute this software and such derivative works * for any purpose, so long as the name of The University of * Michigan is not used in any advertising or publicity * pertaining to the use of distribution of this software * without specific, written prior authorization. If the * above copyright notice or any other identification of the * University of Michigan is included in any copy of any * portion of this software, then the disclaimer below must * also be included. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF * SUCH DAMAGES. */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include "pkinit.h" #ifdef LONGHORN_BETA_COMPAT /* * It is anticipated that all the special checks currently * required when talking to a Longhorn server will go away * by the time it is officially released and all references * to the longhorn global can be removed and any code * #ifdef'd with LONGHORN_BETA_COMPAT can be removed. * * Current testing (20070620) is against a patched Beta 3 * version of Longhorn. Most, if not all, problems should * be fixed in SP1 of Longhorn. */ int longhorn = 0; /* Talking to a Longhorn server? */ #endif krb5_error_code pkinit_client_process (krb5_context context, void *plugin_context, void *request_context, krb5_get_init_creds_opt *gic_opt, preauth_get_client_data_proc get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req * request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *in_padata, krb5_prompter_fct prompter, void *prompter_data, preauth_get_as_key_proc gak_fct, void *gak_data, krb5_data * salt, krb5_data * s2kparams, krb5_keyblock * as_key, krb5_pa_data *** out_padata); krb5_error_code pkinit_client_tryagain (krb5_context context, void *plugin_context, void *request_context, krb5_get_init_creds_opt *gic_opt, preauth_get_client_data_proc get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req * request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *in_padata, krb5_error *err_reply, krb5_prompter_fct prompter, void *prompter_data, preauth_get_as_key_proc gak_fct, void *gak_data, krb5_data * salt, krb5_data * s2kparams, krb5_keyblock * as_key, krb5_pa_data *** out_padata); void pkinit_client_req_init (krb5_context contex, void *plugin_context, void **request_context); void pkinit_client_req_fini (krb5_context context, void *plugin_context, void *request_context); krb5_error_code pa_pkinit_gen_req (krb5_context context, pkinit_context plgctx, pkinit_req_context reqctx, krb5_kdc_req * request, krb5_pa_data * in_padata, krb5_pa_data *** out_padata, krb5_prompter_fct prompter, void *prompter_data, krb5_get_init_creds_opt *gic_opt); krb5_error_code pkinit_as_req_create (krb5_context context, pkinit_context plgctx, pkinit_req_context reqctx, krb5_timestamp ctsec, krb5_int32 cusec, krb5_ui_4 nonce, const krb5_checksum * cksum, krb5_principal server, krb5_data ** as_req); krb5_error_code pkinit_as_rep_parse (krb5_context context, pkinit_context plgctx, pkinit_req_context reqctx, krb5_preauthtype pa_type, krb5_kdc_req * request, const krb5_data * as_rep, krb5_keyblock * key_block, krb5_enctype etype, krb5_data *); krb5_error_code pa_pkinit_parse_rep (krb5_context context, pkinit_context plgctx, pkinit_req_context reqcxt, krb5_kdc_req * request, krb5_pa_data * in_padata, krb5_enctype etype, krb5_keyblock * as_key, krb5_data *); static int pkinit_client_plugin_init(krb5_context context, void **blob); static void pkinit_client_plugin_fini(krb5_context context, void *blob); /* ARGSUSED */ krb5_error_code pa_pkinit_gen_req(krb5_context context, pkinit_context plgctx, pkinit_req_context reqctx, krb5_kdc_req * request, krb5_pa_data * in_padata, krb5_pa_data *** out_padata, krb5_prompter_fct prompter, void *prompter_data, krb5_get_init_creds_opt *gic_opt) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_data *out_data = NULL; krb5_timestamp ctsec = 0; krb5_int32 cusec = 0; krb5_ui_4 nonce = 0; krb5_checksum cksum; krb5_data *der_req = NULL; krb5_pa_data **return_pa_data = NULL; cksum.contents = NULL; reqctx->pa_type = in_padata->pa_type; pkiDebug("kdc_options = 0x%x till = %d\n", request->kdc_options, request->till); /* If we don't have a client, we're done */ if (request->client == NULL) { pkiDebug("No request->client; aborting PKINIT\n"); return KRB5KDC_ERR_PREAUTH_FAILED; } retval = pkinit_get_kdc_cert(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, request->server); if (retval) { pkiDebug("pkinit_get_kdc_cert returned %d\n", retval); goto cleanup; } /* checksum of the encoded KDC-REQ-BODY */ retval = k5int_encode_krb5_kdc_req_body(request, &der_req); if (retval) { pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval); goto cleanup; } retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, der_req, &cksum); if (retval) goto cleanup; #ifdef DEBUG_CKSUM pkiDebug("calculating checksum on buf size (%d)\n", der_req->length); print_buffer(der_req->data, der_req->length); #endif retval = krb5_us_timeofday(context, &ctsec, &cusec); if (retval) goto cleanup; /* XXX PKINIT RFC says that nonce in PKAuthenticator doesn't have be the * same as in the AS_REQ. However, if we pick a different nonce, then we * need to remember that info when AS_REP is returned. I'm choosing to * reuse the AS_REQ nonce. */ nonce = request->nonce; retval = pkinit_as_req_create(context, plgctx, reqctx, ctsec, cusec, nonce, &cksum, request->server, &out_data); if (retval || !out_data->length) { pkiDebug("error %d on pkinit_as_req_create; aborting PKINIT\n", (int) retval); goto cleanup; } retval = ENOMEM; /* * The most we'll return is two pa_data, normally just one. * We need to make room for the NULL terminator. */ return_pa_data = (krb5_pa_data **) malloc(3 * sizeof(krb5_pa_data *)); if (return_pa_data == NULL) goto cleanup; return_pa_data[1] = NULL; /* in case of an early trip to cleanup */ return_pa_data[2] = NULL; /* Terminate the list */ return_pa_data[0] = (krb5_pa_data *) malloc(sizeof(krb5_pa_data)); if (return_pa_data[0] == NULL) goto cleanup; return_pa_data[1] = (krb5_pa_data *) malloc(sizeof(krb5_pa_data)); if (return_pa_data[1] == NULL) goto cleanup; return_pa_data[0]->magic = KV5M_PA_DATA; if (in_padata->pa_type == KRB5_PADATA_PK_AS_REQ_OLD) return_pa_data[0]->pa_type = KRB5_PADATA_PK_AS_REP_OLD; else return_pa_data[0]->pa_type = in_padata->pa_type; return_pa_data[0]->length = out_data->length; return_pa_data[0]->contents = (krb5_octet *) out_data->data; #ifdef LONGHORN_BETA_COMPAT /* * LH Beta 3 requires the extra pa-data, even for RFC requests, * in order to get the Checksum rather than a Nonce in the reply. * This can be removed when LH SP1 is released. */ if ((return_pa_data[0]->pa_type == KRB5_PADATA_PK_AS_REP_OLD && reqctx->opts->win2k_require_cksum) || (longhorn == 1)) { #else if ((return_pa_data[0]->pa_type == KRB5_PADATA_PK_AS_REP_OLD && reqctx->opts->win2k_require_cksum)) { #endif return_pa_data[1]->pa_type = 132; return_pa_data[1]->length = 0; return_pa_data[1]->contents = NULL; } else { free(return_pa_data[1]); return_pa_data[1] = NULL; /* Move the list terminator */ } *out_padata = return_pa_data; retval = 0; cleanup: if (der_req != NULL) krb5_free_data(context, der_req); if (out_data != NULL) free(out_data); if (retval) { if (return_pa_data) { if (return_pa_data[0] != NULL) free(return_pa_data[0]); if (return_pa_data[1] != NULL) free(return_pa_data[1]); free(return_pa_data); } if (out_data) { free(out_data->data); free(out_data); } } return retval; } krb5_error_code pkinit_as_req_create(krb5_context context, pkinit_context plgctx, pkinit_req_context reqctx, krb5_timestamp ctsec, krb5_int32 cusec, krb5_ui_4 nonce, const krb5_checksum * cksum, krb5_principal server, krb5_data ** as_req) { krb5_error_code retval = ENOMEM; krb5_subject_pk_info *info = NULL; krb5_data *coded_auth_pack = NULL; krb5_auth_pack *auth_pack = NULL; krb5_pa_pk_as_req *req = NULL; krb5_auth_pack_draft9 *auth_pack9 = NULL; krb5_pa_pk_as_req_draft9 *req9 = NULL; int protocol = reqctx->opts->dh_or_rsa; pkiDebug("pkinit_as_req_create pa_type = %d\n", reqctx->pa_type); /* Create the authpack */ switch((int)reqctx->pa_type) { case KRB5_PADATA_PK_AS_REQ_OLD: protocol = RSA_PROTOCOL; init_krb5_auth_pack_draft9(&auth_pack9); if (auth_pack9 == NULL) goto cleanup; auth_pack9->pkAuthenticator.ctime = ctsec; auth_pack9->pkAuthenticator.cusec = cusec; auth_pack9->pkAuthenticator.nonce = nonce; auth_pack9->pkAuthenticator.kdcName = server; auth_pack9->pkAuthenticator.kdcRealm.magic = 0; auth_pack9->pkAuthenticator.kdcRealm.data = (unsigned char *)server->realm.data; auth_pack9->pkAuthenticator.kdcRealm.length = server->realm.length; free(cksum->contents); break; case KRB5_PADATA_PK_AS_REQ: init_krb5_subject_pk_info(&info); if (info == NULL) goto cleanup; init_krb5_auth_pack(&auth_pack); if (auth_pack == NULL) goto cleanup; auth_pack->pkAuthenticator.ctime = ctsec; auth_pack->pkAuthenticator.cusec = cusec; auth_pack->pkAuthenticator.nonce = nonce; auth_pack->pkAuthenticator.paChecksum = *cksum; auth_pack->clientDHNonce.length = 0; auth_pack->clientPublicValue = info; /* add List of CMS algorithms */ retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, &auth_pack->supportedCMSTypes); if (retval) goto cleanup; break; default: pkiDebug("as_req: unrecognized pa_type = %d\n", (int)reqctx->pa_type); retval = -1; goto cleanup; } switch(protocol) { case DH_PROTOCOL: pkiDebug("as_req: DH key transport algorithm\n"); retval = pkinit_copy_krb5_octet_data(&info->algorithm.algorithm, &dh_oid); if (retval) { pkiDebug("failed to copy dh_oid\n"); goto cleanup; } /* create client-side DH keys */ if ((retval = client_create_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, reqctx->opts->dh_size, &info->algorithm.parameters.data, &info->algorithm.parameters.length, &info->subjectPublicKey.data, &info->subjectPublicKey.length)) != 0) { pkiDebug("failed to create dh parameters\n"); goto cleanup; } break; case RSA_PROTOCOL: pkiDebug("as_req: RSA key transport algorithm\n"); switch((int)reqctx->pa_type) { case KRB5_PADATA_PK_AS_REQ_OLD: auth_pack9->clientPublicValue = NULL; break; case KRB5_PADATA_PK_AS_REQ: free_krb5_subject_pk_info(&info); auth_pack->clientPublicValue = NULL; break; } break; default: pkiDebug("as_req: unknown key transport protocol %d\n", protocol); retval = -1; goto cleanup; } /* Encode the authpack */ switch((int)reqctx->pa_type) { case KRB5_PADATA_PK_AS_REQ: retval = k5int_encode_krb5_auth_pack(auth_pack, &coded_auth_pack); break; case KRB5_PADATA_PK_AS_REQ_OLD: retval = k5int_encode_krb5_auth_pack_draft9(auth_pack9, &coded_auth_pack); break; } if (retval) { pkiDebug("failed to encode the AuthPack %d\n", retval); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)coded_auth_pack->data, coded_auth_pack->length, "/tmp/client_auth_pack"); #endif /* create PKCS7 object from authpack */ switch((int)reqctx->pa_type) { case KRB5_PADATA_PK_AS_REQ: init_krb5_pa_pk_as_req(&req); if (req == NULL) { retval = ENOMEM; goto cleanup; } retval = cms_signeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_CLIENT, 1, (unsigned char *)coded_auth_pack->data, coded_auth_pack->length, &req->signedAuthPack.data, &req->signedAuthPack.length); #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)req->signedAuthPack.data, req->signedAuthPack.length, "/tmp/client_signed_data"); #endif break; case KRB5_PADATA_PK_AS_REQ_OLD: init_krb5_pa_pk_as_req_draft9(&req9); if (req9 == NULL) { retval = ENOMEM; goto cleanup; } retval = cms_signeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_DRAFT9, 1, (unsigned char *)coded_auth_pack->data, coded_auth_pack->length, &req9->signedAuthPack.data, &req9->signedAuthPack.length); break; #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)req9->signedAuthPack.data, req9->signedAuthPack.length, "/tmp/client_signed_data_draft9"); #endif } krb5_free_data(context, coded_auth_pack); if (retval) { pkiDebug("failed to create pkcs7 signed data\n"); goto cleanup; } /* create a list of trusted CAs */ switch((int)reqctx->pa_type) { case KRB5_PADATA_PK_AS_REQ: retval = create_krb5_trustedCertifiers(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, &req->trustedCertifiers); if (retval) goto cleanup; retval = create_issuerAndSerial(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, &req->kdcPkId.data, &req->kdcPkId.length); if (retval) goto cleanup; /* Encode the as-req */ retval = k5int_encode_krb5_pa_pk_as_req(req, as_req); break; case KRB5_PADATA_PK_AS_REQ_OLD: #if 0 /* W2K3 KDC doesn't like this */ retval = create_krb5_trustedCas(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, 1, &req9->trustedCertifiers); if (retval) goto cleanup; #endif retval = create_issuerAndSerial(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, &req9->kdcCert.data, &req9->kdcCert.length); if (retval) goto cleanup; /* Encode the as-req */ retval = k5int_encode_krb5_pa_pk_as_req_draft9(req9, as_req); break; } #ifdef DEBUG_ASN1 if (!retval) print_buffer_bin((unsigned char *)(*as_req)->data, (*as_req)->length, "/tmp/client_as_req"); #endif cleanup: switch((int)reqctx->pa_type) { case KRB5_PADATA_PK_AS_REQ: free_krb5_auth_pack(&auth_pack); free_krb5_pa_pk_as_req(&req); break; case KRB5_PADATA_PK_AS_REQ_OLD: free_krb5_pa_pk_as_req_draft9(&req9); free(auth_pack9); break; } pkiDebug("pkinit_as_req_create retval=%d\n", (int) retval); return retval; } krb5_error_code pa_pkinit_parse_rep(krb5_context context, pkinit_context plgctx, pkinit_req_context reqctx, krb5_kdc_req * request, krb5_pa_data * in_padata, krb5_enctype etype, krb5_keyblock * as_key, krb5_data *encoded_request) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_data asRep = { 0, 0, NULL}; /* * One way or the other - success or failure - no other PA systems can * work if the server sent us a PKINIT reply, since only we know how to * decrypt the key. */ if ((in_padata == NULL) || (in_padata->length == 0)) { pkiDebug("pa_pkinit_parse_rep: no in_padata\n"); return KRB5KDC_ERR_PREAUTH_FAILED; } asRep.data = (char *) in_padata->contents; asRep.length = in_padata->length; retval = pkinit_as_rep_parse(context, plgctx, reqctx, in_padata->pa_type, request, &asRep, as_key, etype, encoded_request); if (retval) { pkiDebug("pkinit_as_rep_parse returned %d (%s)\n", retval, error_message(retval)); goto cleanup; } retval = 0; cleanup: return retval; } static krb5_error_code verify_kdc_san(krb5_context context, pkinit_context plgctx, pkinit_req_context reqctx, krb5_principal kdcprinc, int *valid_san, int *need_eku_checking) { krb5_error_code retval; char **certhosts = NULL, **cfghosts = NULL; krb5_principal *princs = NULL; unsigned char ***get_dns; int i, j; *valid_san = 0; *need_eku_checking = 1; retval = pkinit_libdefault_strings(context, krb5_princ_realm(context, kdcprinc), "pkinit_kdc_hostname", &cfghosts); if (retval || cfghosts == NULL) { pkiDebug("%s: No pkinit_kdc_hostname values found in config file\n", __FUNCTION__); get_dns = NULL; } else { pkiDebug("%s: pkinit_kdc_hostname values found in config file\n", __FUNCTION__); get_dns = (unsigned char ***)&certhosts; } retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, &princs, NULL, get_dns); if (retval) { pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__); retval = KRB5KDC_ERR_KDC_NAME_MISMATCH; goto out; } #if 0 retval = call_san_checking_plugins(context, plgctx, reqctx, idctx, princs, hosts, &plugin_decision, need_eku_checking); pkiDebug("%s: call_san_checking_plugins() returned retval %d\n", __FUNCTION__); if (retval) { retval = KRB5KDC_ERR_KDC_NAME_MISMATCH; goto out; } pkiDebug("%s: call_san_checking_plugins() returned decision %d and " "need_eku_checking %d\n", __FUNCTION__, plugin_decision, *need_eku_checking); if (plugin_decision != NO_DECISION) { retval = plugin_decision; goto out; } #endif pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__); for (i = 0; princs != NULL && princs[i] != NULL; i++) { if (krb5_principal_compare(context, princs[i], kdcprinc)) { pkiDebug("%s: pkinit san match found\n", __FUNCTION__); *valid_san = 1; *need_eku_checking = 0; retval = 0; goto out; } } pkiDebug("%s: no pkinit san match found\n", __FUNCTION__); if (certhosts == NULL) { pkiDebug("%s: no certhosts (or we wouldn't accept them anyway)\n", __FUNCTION__); retval = KRB5KDC_ERR_KDC_NAME_MISMATCH; goto out; } for (i = 0; certhosts[i] != NULL; i++) { for (j = 0; cfghosts != NULL && cfghosts[j] != NULL; j++) { pkiDebug("%s: comparing cert name '%s' with config name '%s'\n", __FUNCTION__, certhosts[i], cfghosts[j]); if (strcmp(certhosts[i], cfghosts[j]) == 0) { pkiDebug("%s: we have a dnsName match\n", __FUNCTION__); *valid_san = 1; retval = 0; goto out; } } } pkiDebug("%s: no dnsName san match found\n", __FUNCTION__); /* We found no match */ retval = 0; out: if (princs != NULL) { for (i = 0; princs[i] != NULL; i++) krb5_free_principal(context, princs[i]); free(princs); } if (certhosts != NULL) { for (i = 0; certhosts[i] != NULL; i++) free(certhosts[i]); free(certhosts); } if (cfghosts != NULL) profile_free_list(cfghosts); pkiDebug("%s: returning retval %d, valid_san %d, need_eku_checking %d\n", __FUNCTION__, retval, *valid_san, *need_eku_checking); return retval; } static krb5_error_code verify_kdc_eku(krb5_context context, pkinit_context plgctx, pkinit_req_context reqctx, int *eku_accepted) { krb5_error_code retval; *eku_accepted = 0; if (reqctx->opts->require_eku == 0) { pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__); *eku_accepted = 1; retval = 0; goto out; } retval = crypto_check_cert_eku(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, 1, /* kdc cert */ reqctx->opts->accept_secondary_eku, eku_accepted); if (retval) { pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n", __FUNCTION__, retval, error_message(retval)); goto out; } out: pkiDebug("%s: returning retval %d, eku_accepted %d\n", __FUNCTION__, retval, *eku_accepted); return retval; } /* * Parse PA-PK-AS-REP message. Optionally evaluates the message's * certificate chain. * Optionally returns various components. */ krb5_error_code pkinit_as_rep_parse(krb5_context context, pkinit_context plgctx, pkinit_req_context reqctx, krb5_preauthtype pa_type, krb5_kdc_req *request, const krb5_data *as_rep, krb5_keyblock *key_block, krb5_enctype etype, krb5_data *encoded_request) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_pa_pk_as_rep *kdc_reply = NULL; krb5_kdc_dh_key_info *kdc_dh = NULL; krb5_reply_key_pack *key_pack = NULL; krb5_reply_key_pack_draft9 *key_pack9 = NULL; krb5_octet_data dh_data = { 0, 0, NULL }; unsigned char *client_key = NULL, *kdc_hostname = NULL; unsigned int client_key_len = 0; krb5_checksum cksum = {0, 0, 0, NULL}; krb5_data k5data; int valid_san = 0; int valid_eku = 0; int need_eku_checking = 1; assert((as_rep != NULL) && (key_block != NULL)); #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)as_rep->data, as_rep->length, "/tmp/client_as_rep"); #endif if ((retval = k5int_decode_krb5_pa_pk_as_rep(as_rep, &kdc_reply))) { pkiDebug("decode_krb5_as_rep failed %d\n", retval); return retval; } switch(kdc_reply->choice) { case choice_pa_pk_as_rep_dhInfo: pkiDebug("as_rep: DH key transport algorithm\n"); #ifdef DEBUG_ASN1 print_buffer_bin(kdc_reply->u.dh_Info.dhSignedData.data, kdc_reply->u.dh_Info.dhSignedData.length, "/tmp/client_kdc_signeddata"); #endif if ((retval = cms_signeddata_verify(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, CMS_SIGN_SERVER, reqctx->opts->require_crl_checking, kdc_reply->u.dh_Info.dhSignedData.data, kdc_reply->u.dh_Info.dhSignedData.length, &dh_data.data, &dh_data.length, NULL, NULL)) != 0) { pkiDebug("failed to verify pkcs7 signed data\n"); goto cleanup; } break; case choice_pa_pk_as_rep_encKeyPack: pkiDebug("as_rep: RSA key transport algorithm\n"); if ((retval = cms_envelopeddata_verify(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, pa_type, reqctx->opts->require_crl_checking, kdc_reply->u.encKeyPack.data, kdc_reply->u.encKeyPack.length, &dh_data.data, &dh_data.length)) != 0) { pkiDebug("failed to verify pkcs7 enveloped data\n"); goto cleanup; } break; default: pkiDebug("unknown as_rep type %d\n", kdc_reply->choice); retval = -1; goto cleanup; } retval = verify_kdc_san(context, plgctx, reqctx, request->server, &valid_san, &need_eku_checking); if (retval) goto cleanup; if (!valid_san) { pkiDebug("%s: did not find an acceptable SAN in KDC certificate\n", __FUNCTION__); retval = KRB5KDC_ERR_KDC_NAME_MISMATCH; goto cleanup; } if (need_eku_checking) { retval = verify_kdc_eku(context, plgctx, reqctx, &valid_eku); if (retval) goto cleanup; if (!valid_eku) { pkiDebug("%s: did not find an acceptable EKU in KDC certificate\n", __FUNCTION__); retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; goto cleanup; } } else pkiDebug("%s: skipping EKU check\n", __FUNCTION__); OCTETDATA_TO_KRB5DATA(&dh_data, &k5data); switch(kdc_reply->choice) { case choice_pa_pk_as_rep_dhInfo: #ifdef DEBUG_ASN1 print_buffer_bin(dh_data.data, dh_data.length, "/tmp/client_dh_key"); #endif if ((retval = k5int_decode_krb5_kdc_dh_key_info(&k5data, &kdc_dh)) != 0) { pkiDebug("failed to decode kdc_dh_key_info\n"); goto cleanup; } /* client after KDC reply */ if ((retval = client_process_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, kdc_dh->subjectPublicKey.data, kdc_dh->subjectPublicKey.length, &client_key, &client_key_len)) != 0) { pkiDebug("failed to process dh params\n"); goto cleanup; } retval = pkinit_octetstring2key(context, etype, client_key, client_key_len, key_block); if (retval) { pkiDebug("failed to create key pkinit_octetstring2key %s\n", error_message(retval)); goto cleanup; } break; case choice_pa_pk_as_rep_encKeyPack: #ifdef DEBUG_ASN1 print_buffer_bin(dh_data.data, dh_data.length, "/tmp/client_key_pack"); #endif if ((retval = k5int_decode_krb5_reply_key_pack(&k5data, &key_pack)) != 0) { pkiDebug("failed to decode reply_key_pack\n"); #ifdef LONGHORN_BETA_COMPAT /* * LH Beta 3 requires the extra pa-data, even for RFC requests, * in order to get the Checksum rather than a Nonce in the reply. * This can be removed when LH SP1 is released. */ if (pa_type == KRB5_PADATA_PK_AS_REP && longhorn == 0) #else if (pa_type == KRB5_PADATA_PK_AS_REP) #endif goto cleanup; else { if ((retval = k5int_decode_krb5_reply_key_pack_draft9(&k5data, &key_pack9)) != 0) { pkiDebug("failed to decode reply_key_pack_draft9\n"); goto cleanup; } pkiDebug("decode reply_key_pack_draft9\n"); if (key_pack9->nonce != request->nonce) { pkiDebug("nonce in AS_REP=%d doesn't match AS_REQ=%d\n", key_pack9->nonce, request->nonce); retval = -1; goto cleanup; } krb5_copy_keyblock_contents(context, &key_pack9->replyKey, key_block); break; } } /* * This is hack but Windows sends back SHA1 checksum * with checksum type of 14. There is currently no * checksum type of 14 defined. */ if (key_pack->asChecksum.checksum_type == 14) key_pack->asChecksum.checksum_type = CKSUMTYPE_NIST_SHA; retval = krb5_c_make_checksum(context, key_pack->asChecksum.checksum_type, &key_pack->replyKey, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, encoded_request, &cksum); if (retval) { pkiDebug("failed to make a checksum\n"); goto cleanup; } if ((cksum.length != key_pack->asChecksum.length) || memcmp(cksum.contents, key_pack->asChecksum.contents, cksum.length)) { pkiDebug("failed to match the checksums\n"); #ifdef DEBUG_CKSUM pkiDebug("calculating checksum on buf size (%d)\n", encoded_request->length); print_buffer(encoded_request->data, encoded_request->length); pkiDebug("encrypting key (%d)\n", key_pack->replyKey.length); print_buffer(key_pack->replyKey.contents, key_pack->replyKey.length); pkiDebug("received checksum type=%d size=%d ", key_pack->asChecksum.checksum_type, key_pack->asChecksum.length); print_buffer(key_pack->asChecksum.contents, key_pack->asChecksum.length); pkiDebug("expected checksum type=%d size=%d ", cksum.checksum_type, cksum.length); print_buffer(cksum.contents, cksum.length); #endif goto cleanup; } else pkiDebug("checksums match\n"); krb5_copy_keyblock_contents(context, &key_pack->replyKey, key_block); break; default: pkiDebug("unknow as_rep type %d\n", kdc_reply->choice); goto cleanup; } retval = 0; cleanup: if (dh_data.data != NULL) free(dh_data.data); if (client_key != NULL) free(client_key); free_krb5_kdc_dh_key_info(&kdc_dh); free_krb5_pa_pk_as_rep(&kdc_reply); if (key_pack != NULL) { free_krb5_reply_key_pack(&key_pack); if (cksum.contents != NULL) free(cksum.contents); } if (key_pack9 != NULL) free_krb5_reply_key_pack_draft9(&key_pack9); if (kdc_hostname != NULL) free(kdc_hostname); pkiDebug("pkinit_as_rep_parse returning %d (%s)\n", retval, error_message(retval)); return retval; } static void pkinit_client_profile(krb5_context context, pkinit_context plgctx, pkinit_req_context reqctx, krb5_kdc_req *request) { char *eku_string = NULL; pkiDebug("pkinit_client_profile %p %p %p %p\n", context, plgctx, reqctx, request); (void) pkinit_libdefault_boolean(context, &request->server->realm, "pkinit_win2k", reqctx->opts->win2k_target, &reqctx->opts->win2k_target); (void) pkinit_libdefault_boolean(context, &request->server->realm, "pkinit_win2k_require_binding", reqctx->opts->win2k_require_cksum, &reqctx->opts->win2k_require_cksum); (void) pkinit_libdefault_boolean(context, &request->server->realm, "pkinit_require_crl_checking", reqctx->opts->require_crl_checking, &reqctx->opts->require_crl_checking); (void) pkinit_libdefault_integer(context, &request->server->realm, "pkinit_dh_min_bits", reqctx->opts->dh_size, &reqctx->opts->dh_size); if (reqctx->opts->dh_size != 1024 && reqctx->opts->dh_size != 2048 && reqctx->opts->dh_size != 4096) { pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, " "using default value (%d) instead\n", __FUNCTION__, reqctx->opts->dh_size, PKINIT_DEFAULT_DH_MIN_BITS); reqctx->opts->dh_size = PKINIT_DEFAULT_DH_MIN_BITS; } (void) pkinit_libdefault_string(context, &request->server->realm, "pkinit_eku_checking", &eku_string); if (eku_string != NULL) { if (strcasecmp(eku_string, "kpKDC") == 0) { reqctx->opts->require_eku = 1; reqctx->opts->accept_secondary_eku = 0; } else if (strcasecmp(eku_string, "kpServerAuth") == 0) { reqctx->opts->require_eku = 1; reqctx->opts->accept_secondary_eku = 1; } else if (strcasecmp(eku_string, "none") == 0) { reqctx->opts->require_eku = 0; reqctx->opts->accept_secondary_eku = 0; } else { pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n", __FUNCTION__, eku_string); } free(eku_string); } #ifdef LONGHORN_BETA_COMPAT /* Temporarily just set global flag from config file */ (void) pkinit_libdefault_boolean(context, &request->server->realm, "pkinit_longhorn", 0, &longhorn); #endif /* Only process anchors here if they were not specified on command line */ if (reqctx->idopts->anchors == NULL) (void) pkinit_libdefault_strings(context, &request->server->realm, "pkinit_anchors", &reqctx->idopts->anchors); /* Solaris Kerberos */ (void) pkinit_libdefault_strings(context, &request->server->realm, "pkinit_pool", &reqctx->idopts->intermediates); (void) pkinit_libdefault_strings(context, &request->server->realm, "pkinit_revoke", &reqctx->idopts->crls); (void) pkinit_libdefault_strings(context, &request->server->realm, "pkinit_identities", &reqctx->idopts->identity_alt); } /* ARGSUSED */ krb5_error_code pkinit_client_process(krb5_context context, void *plugin_context, void *request_context, krb5_get_init_creds_opt *gic_opt, preauth_get_client_data_proc get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *in_padata, krb5_prompter_fct prompter, void *prompter_data, preauth_get_as_key_proc gak_fct, void *gak_data, krb5_data *salt, krb5_data *s2kparams, krb5_keyblock *as_key, krb5_pa_data ***out_padata) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_enctype enctype = -1; krb5_data *cdata = NULL; int processing_request = 0; pkinit_context plgctx = (pkinit_context)plugin_context; pkinit_req_context reqctx = (pkinit_req_context)request_context; pkiDebug("pkinit_client_process %p %p %p %p\n", context, plgctx, reqctx, request); if (plgctx == NULL || reqctx == NULL) return EINVAL; switch ((int) in_padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n"); processing_request = 1; break; case KRB5_PADATA_PK_AS_REP: pkiDebug("processing KRB5_PADATA_PK_AS_REP\n"); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: if (in_padata->length == 0) { pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n"); in_padata->pa_type = KRB5_PADATA_PK_AS_REQ_OLD; processing_request = 1; } else { pkiDebug("processing KRB5_PADATA_PK_AS_REP_OLD\n"); in_padata->pa_type = KRB5_PADATA_PK_AS_REP_OLD; } break; default: pkiDebug("unrecognized patype = %d for PKINIT\n", in_padata->pa_type); return EINVAL; } if (processing_request) { pkinit_client_profile(context, plgctx, reqctx, request); /* Solaris Kerberos */ retval = pkinit_identity_set_prompter(reqctx->idctx, prompter, prompter_data); if (retval) { pkiDebug("pkinit_identity_set_prompter returned %d (%s)\n", retval, error_message(retval)); return retval; } retval = pkinit_identity_initialize(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idopts, reqctx->idctx, 1, request->client); if (retval) { pkiDebug("pkinit_identity_initialize returned %d (%s)\n", retval, error_message(retval)); return retval; } retval = pa_pkinit_gen_req(context, plgctx, reqctx, request, in_padata, out_padata, prompter, prompter_data, gic_opt); } else { /* * Get the enctype of the reply. */ retval = (*get_data_proc)(context, rock, krb5plugin_preauth_client_get_etype, &cdata); if (retval) { pkiDebug("get_data_proc returned %d (%s)\n", retval, error_message(retval)); return retval; } enctype = *((krb5_enctype *)cdata->data); (*get_data_proc)(context, rock, krb5plugin_preauth_client_free_etype, &cdata); retval = pa_pkinit_parse_rep(context, plgctx, reqctx, request, in_padata, enctype, as_key, encoded_previous_request); } pkiDebug("pkinit_client_process: returning %d (%s)\n", retval, error_message(retval)); return retval; } /* ARGSUSED */ krb5_error_code pkinit_client_tryagain(krb5_context context, void *plugin_context, void *request_context, krb5_get_init_creds_opt *gic_opt, preauth_get_client_data_proc get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *in_padata, krb5_error *err_reply, krb5_prompter_fct prompter, void *prompter_data, preauth_get_as_key_proc gak_fct, void *gak_data, krb5_data *salt, krb5_data *s2kparams, krb5_keyblock *as_key, krb5_pa_data ***out_padata) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; pkinit_context plgctx = (pkinit_context)plugin_context; pkinit_req_context reqctx = (pkinit_req_context)request_context; krb5_typed_data **typed_data = NULL; krb5_data scratch; krb5_external_principal_identifier **krb5_trusted_certifiers = NULL; krb5_algorithm_identifier **algId = NULL; int do_again = 0; pkiDebug("pkinit_client_tryagain %p %p %p %p\n", context, plgctx, reqctx, request); if (reqctx->pa_type != in_padata->pa_type) return retval; #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)err_reply->e_data.data, err_reply->e_data.length, "/tmp/client_edata"); #endif retval = k5int_decode_krb5_typed_data(&err_reply->e_data, &typed_data); if (retval) { pkiDebug("decode_krb5_typed_data failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin(typed_data[0]->data, typed_data[0]->length, "/tmp/client_typed_data"); #endif OCTETDATA_TO_KRB5DATA(typed_data[0], &scratch); switch(typed_data[0]->type) { case TD_TRUSTED_CERTIFIERS: case TD_INVALID_CERTIFICATES: retval = k5int_decode_krb5_td_trusted_certifiers(&scratch, &krb5_trusted_certifiers); if (retval) { pkiDebug("failed to decode sequence of trusted certifiers\n"); goto cleanup; } retval = pkinit_process_td_trusted_certifiers(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, krb5_trusted_certifiers, typed_data[0]->type); if (!retval) do_again = 1; break; case TD_DH_PARAMETERS: retval = k5int_decode_krb5_td_dh_parameters(&scratch, &algId); if (retval) { pkiDebug("failed to decode td_dh_parameters\n"); goto cleanup; } retval = pkinit_process_td_dh_params(context, plgctx->cryptoctx, reqctx->cryptoctx, reqctx->idctx, algId, &reqctx->opts->dh_size); if (!retval) do_again = 1; break; default: break; } if (do_again) { retval = pa_pkinit_gen_req(context, plgctx, reqctx, request, in_padata, out_padata, prompter, prompter_data, gic_opt); if (retval) goto cleanup; } retval = 0; cleanup: if (krb5_trusted_certifiers != NULL) free_krb5_external_principal_identifier(&krb5_trusted_certifiers); if (typed_data != NULL) free_krb5_typed_data(&typed_data); if (algId != NULL) free_krb5_algorithm_identifiers(&algId); pkiDebug("pkinit_client_tryagain: returning %d (%s)\n", retval, error_message(retval)); return retval; } /* ARGSUSED */ static int pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype) { return PA_REAL; } static krb5_preauthtype supported_client_pa_types[] = { KRB5_PADATA_PK_AS_REP, KRB5_PADATA_PK_AS_REQ, KRB5_PADATA_PK_AS_REP_OLD, KRB5_PADATA_PK_AS_REQ_OLD, 0 }; /* ARGSUSED */ void pkinit_client_req_init(krb5_context context, void *plugin_context, void **request_context) { krb5_error_code retval = ENOMEM; struct _pkinit_req_context *reqctx = NULL; struct _pkinit_context *plgctx = (struct _pkinit_context *)plugin_context; *request_context = NULL; reqctx = (struct _pkinit_req_context *) malloc(sizeof(*reqctx)); if (reqctx == NULL) return; (void) memset(reqctx, 0, sizeof(*reqctx)); reqctx->magic = PKINIT_REQ_CTX_MAGIC; reqctx->cryptoctx = NULL; reqctx->opts = NULL; reqctx->idctx = NULL; reqctx->idopts = NULL; retval = pkinit_init_req_opts(&reqctx->opts); if (retval) goto cleanup; reqctx->opts->require_eku = plgctx->opts->require_eku; reqctx->opts->accept_secondary_eku = plgctx->opts->accept_secondary_eku; reqctx->opts->dh_or_rsa = plgctx->opts->dh_or_rsa; reqctx->opts->allow_upn = plgctx->opts->allow_upn; reqctx->opts->require_crl_checking = plgctx->opts->require_crl_checking; retval = pkinit_init_req_crypto(&reqctx->cryptoctx); if (retval) goto cleanup; retval = pkinit_init_identity_crypto(&reqctx->idctx); if (retval) goto cleanup; retval = pkinit_dup_identity_opts(plgctx->idopts, &reqctx->idopts); if (retval) goto cleanup; *request_context = (void *) reqctx; pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx); cleanup: if (retval) { if (reqctx->idctx != NULL) pkinit_fini_identity_crypto(reqctx->idctx); if (reqctx->cryptoctx != NULL) pkinit_fini_req_crypto(reqctx->cryptoctx); if (reqctx->opts != NULL) pkinit_fini_req_opts(reqctx->opts); if (reqctx->idopts != NULL) pkinit_fini_identity_opts(reqctx->idopts); free(reqctx); } return; } /* ARGSUSED */ void pkinit_client_req_fini(krb5_context context, void *plugin_context, void *request_context) { struct _pkinit_req_context *reqctx = (struct _pkinit_req_context *)request_context; pkiDebug("%s: received reqctx at %p\n", __FUNCTION__, reqctx); if (reqctx == NULL) return; if (reqctx->magic != PKINIT_REQ_CTX_MAGIC) { pkiDebug("%s: Bad magic value (%x) in req ctx\n", __FUNCTION__, reqctx->magic); return; } if (reqctx->opts != NULL) pkinit_fini_req_opts(reqctx->opts); if (reqctx->cryptoctx != NULL) pkinit_fini_req_crypto(reqctx->cryptoctx); if (reqctx->idctx != NULL) pkinit_fini_identity_crypto(reqctx->idctx); if (reqctx->idopts != NULL) pkinit_fini_identity_opts(reqctx->idopts); free(reqctx); return; } /* ARGSUSED */ static void pkinit_fini_client_profile(krb5_context context, pkinit_context plgctx) { /* This should clean up anything allocated in pkinit_init_client_profile */ } /* ARGSUSED */ static krb5_error_code pkinit_init_client_profile(krb5_context context, pkinit_context plgctx) { return 0; } static int pkinit_client_plugin_init(krb5_context context, void **blob) { krb5_error_code retval = ENOMEM; struct _pkinit_context *ctx = NULL; ctx = (struct _pkinit_context *)calloc(1, sizeof(*ctx)); if (ctx == NULL) return ENOMEM; (void) memset(ctx, 0, sizeof(*ctx)); ctx->magic = PKINIT_CTX_MAGIC; ctx->opts = NULL; ctx->cryptoctx = NULL; ctx->idopts = NULL; retval = pkinit_accessor_init(); if (retval) goto errout; retval = pkinit_init_plg_opts(&ctx->opts); if (retval) goto errout; retval = pkinit_init_plg_crypto(&ctx->cryptoctx); if (retval) goto errout; retval = pkinit_init_identity_opts(&ctx->idopts); if (retval) goto errout; retval = pkinit_init_client_profile(context, ctx); if (retval) goto errout; *blob = ctx; pkiDebug("%s: returning plgctx at %p\n", __FUNCTION__, ctx); errout: if (retval) pkinit_client_plugin_fini(context, ctx); return retval; } static void pkinit_client_plugin_fini(krb5_context context, void *blob) { struct _pkinit_context *ctx = (struct _pkinit_context *)blob; if (ctx == NULL || ctx->magic != PKINIT_CTX_MAGIC) { pkiDebug("pkinit_lib_fini: got bad plgctx (%p)!\n", ctx); return; } pkiDebug("%s: got plgctx at %p\n", __FUNCTION__, ctx); pkinit_fini_client_profile(context, ctx); pkinit_fini_identity_opts(ctx->idopts); pkinit_fini_plg_crypto(ctx->cryptoctx); pkinit_fini_plg_opts(ctx->opts); free(ctx); } /* ARGSUSED */ static krb5_error_code add_string_to_array(krb5_context context, char ***array, const char *addition) { char **out = NULL; if (*array == NULL) { out = malloc(2 * sizeof(char *)); if (out == NULL) return ENOMEM; out[1] = NULL; out[0] = strdup(addition); if (out[0] == NULL) { free(out); return ENOMEM; } } else { int i; char **a = *array; for (i = 0; a[i] != NULL; i++); out = malloc( (i + 2) * sizeof(char *)); if (out == NULL) return ENOMEM; for (i = 0; a[i] != NULL; i++) { out[i] = a[i]; } out[i++] = strdup(addition); if (out == NULL) { free(out); return ENOMEM; } out[i] = NULL; free(*array); } *array = out; return 0; } static krb5_error_code handle_gic_opt(krb5_context context, struct _pkinit_context *plgctx, const char *attr, const char *value) { krb5_error_code retval; if (strcmp(attr, "X509_user_identity") == 0) { if (plgctx->idopts->identity != NULL) { krb5_set_error_message(context, KRB5_PREAUTH_FAILED, "X509_user_identity can not be given twice\n"); return KRB5_PREAUTH_FAILED; } plgctx->idopts->identity = strdup(value); if (plgctx->idopts->identity == NULL) { krb5_set_error_message(context, ENOMEM, "Could not duplicate X509_user_identity value\n"); return ENOMEM; } } else if (strcmp(attr, "X509_anchors") == 0) { retval = add_string_to_array(context, &plgctx->idopts->anchors, value); if (retval) return retval; } else if (strcmp(attr, "flag_RSA_PROTOCOL") == 0) { if (strcmp(value, "yes") == 0) { pkiDebug("Setting flag to use RSA_PROTOCOL\n"); plgctx->opts->dh_or_rsa = RSA_PROTOCOL; } } else if (strcmp(attr, "PIN") == 0) { /* Solaris Kerberos: handle our PIN attr */ plgctx->idopts->PIN = strdup(value); if (plgctx->idopts->PIN == NULL) return ENOMEM; } return 0; } /* ARGSUSED */ static krb5_error_code pkinit_client_gic_opt(krb5_context context, void *plugin_context, krb5_get_init_creds_opt *gic_opt, const char *attr, const char *value) { krb5_error_code retval; struct _pkinit_context *plgctx = (struct _pkinit_context *)plugin_context; pkiDebug("(pkinit) received '%s' = '%s'\n", attr, value); retval = handle_gic_opt(context, plgctx, attr, value); if (retval) return retval; return 0; } struct krb5plugin_preauth_client_ftable_v1 preauthentication_client_1 = { "pkinit", /* name */ supported_client_pa_types, /* pa_type_list */ NULL, /* enctype_list */ pkinit_client_plugin_init, /* (*init) */ pkinit_client_plugin_fini, /* (*fini) */ pkinit_client_get_flags, /* (*flags) */ pkinit_client_req_init, /* (*client_req_init) */ pkinit_client_req_fini, /* (*client_req_fini) */ pkinit_client_process, /* (*process) */ pkinit_client_tryagain, /* (*tryagain) */ pkinit_client_gic_opt /* (*gic_opt) */ }; /* * COPYRIGHT (C) 2007 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN * ALL RIGHTS RESERVED * * Permission is granted to use, copy, create derivative works * and redistribute this software and such derivative works * for any purpose, so long as the name of The University of * Michigan is not used in any advertising or publicity * pertaining to the use of distribution of this software * without specific, written prior authorization. If the * above copyright notice or any other identification of the * University of Michigan is included in any copy of any * portion of this software, then the disclaimer below must * also be included. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF * SUCH DAMAGES. */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * This header defines the cryptographic interface */ #ifndef _PKINIT_CRYPTO_H #define _PKINIT_CRYPTO_H /* Solaris Kerberos */ #include #include #include #include #include "pkinit_accessor.h" /* * these describe the CMS message types */ enum cms_msg_types { CMS_SIGN_CLIENT, CMS_SIGN_DRAFT9, CMS_SIGN_SERVER, CMS_ENVEL_SERVER }; /* * storage types for identity information */ #define IDTYPE_FILE 1 #define IDTYPE_DIR 2 #define IDTYPE_PKCS11 3 #define IDTYPE_ENVVAR 4 #define IDTYPE_PKCS12 5 /* * ca/crl types */ #define CATYPE_ANCHORS 1 #define CATYPE_INTERMEDIATES 2 #define CATYPE_CRLS 3 /* * The following represent Key Usage values that we * may care about in a certificate */ #define PKINIT_KU_DIGITALSIGNATURE 0x80000000 #define PKINIT_KU_KEYENCIPHERMENT 0x40000000 /* * The following represent Extended Key Usage oid values * that we may care about in a certificate */ #define PKINIT_EKU_PKINIT 0x80000000 #define PKINIT_EKU_MSSCLOGIN 0x40000000 #define PKINIT_EKU_CLIENTAUTH 0x20000000 #define PKINIT_EKU_EMAILPROTECTION 0x10000000 /* Handle to cert, opaque above crypto interface */ typedef struct _pkinit_cert_info *pkinit_cert_handle; /* Handle to cert iteration information, opaque above crypto interface */ typedef struct _pkinit_cert_iter_info *pkinit_cert_iter_handle; #define PKINIT_ITER_NO_MORE 0x11111111 /* XXX */ typedef struct _pkinit_cert_matching_data { pkinit_cert_handle ch; /* cert handle for this certificate */ char *subject_dn; /* rfc2253-style subject name string */ char *issuer_dn; /* rfc2253-style issuer name string */ unsigned int ku_bits; /* key usage information */ unsigned int eku_bits; /* extended key usage information */ krb5_principal *sans; /* Null-terminated array of subject alternative name info (pkinit and ms-upn) */ } pkinit_cert_matching_data; /* * Functions to initialize and cleanup crypto contexts */ krb5_error_code pkinit_init_plg_crypto(pkinit_plg_crypto_context *); void pkinit_fini_plg_crypto(pkinit_plg_crypto_context); krb5_error_code pkinit_init_req_crypto(pkinit_req_crypto_context *); void pkinit_fini_req_crypto(pkinit_req_crypto_context); krb5_error_code pkinit_init_identity_crypto(pkinit_identity_crypto_context *); void pkinit_fini_identity_crypto(pkinit_identity_crypto_context); /* * this function creates a CMS message where eContentType is SignedData */ krb5_error_code cms_signeddata_create (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ int cms_msg_type, /* IN specifies CMS_SIGN_CLIENT for client-side CMS message and CMS_SIGN_SERVER for kdc-side */ int include_certchain, /* IN specifies where certificates field in SignedData should contain certificate path */ unsigned char *auth_pack, /* IN contains DER encoded AuthPack (CMS_SIGN_CLIENT) or DER encoded DHRepInfo (CMS_SIGN_SERVER) */ unsigned int auth_pack_len, /* IN contains length of auth_pack */ unsigned char **signed_data, /* OUT for CMS_SIGN_CLIENT receives DER encoded SignedAuthPack (CMS_SIGN_CLIENT) or DER encoded DHInfo (CMS_SIGN_SERVER) */ unsigned int *signed_data_len); /* OUT receives length of signed_data */ /* * this function verifies a CMS message where eContentType is SignedData */ krb5_error_code cms_signeddata_verify (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ int cms_msg_type, /* IN specifies CMS_SIGN_CLIENT for client-side CMS message and CMS_SIGN_SERVER for kdc-side */ int require_crl_checking, /* IN specifies whether CRL checking should be strictly enforced, i.e. if no CRLs available for the CA then fail verification. note, if the value is 0, crls are still checked if present */ unsigned char *signed_data, /* IN contains DER encoded SignedAuthPack (CMS_SIGN_CLIENT) or DER encoded DHInfo (CMS_SIGN_SERVER) */ unsigned int signed_data_len, /* IN contains length of signed_data*/ unsigned char **auth_pack, /* OUT receives DER encoded AuthPack (CMS_SIGN_CLIENT) or DER encoded DHRepInfo (CMS_SIGN_SERVER)*/ unsigned int *auth_pack_len, /* OUT receives length of auth_pack */ unsigned char **authz_data, /* OUT receives required authorization data that contains the verified certificate chain (only used by the KDC) */ unsigned int *authz_data_len); /* OUT receives length of authz_data */ /* * this function creates a CMS message where eContentType is EnvelopedData */ krb5_error_code cms_envelopeddata_create (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ krb5_preauthtype pa_type, /* IN */ int include_certchain, /* IN specifies whether the certificates field in SignedData should contain certificate path */ unsigned char *key_pack, /* IN contains DER encoded ReplyKeyPack */ unsigned int key_pack_len, /* IN contains length of key_pack */ unsigned char **envel_data, /* OUT receives DER encoded encKeyPack */ unsigned int *envel_data_len); /* OUT receives length of envel_data */ /* * this function creates a CMS message where eContentType is EnvelopedData */ krb5_error_code cms_envelopeddata_verify (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ krb5_preauthtype pa_type, /* IN */ int require_crl_checking, /* IN specifies whether CRL checking should be strictly enforced */ unsigned char *envel_data, /* IN contains DER encoded encKeyPack */ unsigned int envel_data_len, /* IN contains length of envel_data */ unsigned char **signed_data, /* OUT receives ReplyKeyPack */ unsigned int *signed_data_len); /* OUT receives length of signed_data */ /* * this function returns SAN information found in the * received certificate. at least one of pkinit_sans, * upn_sans, or kdc_hostnames must be non-NULL. */ krb5_error_code crypto_retrieve_cert_sans (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ krb5_principal **pkinit_sans, /* OUT if non-NULL, a null-terminated array of id-pkinit-san values found in the certificate are returned */ krb5_principal **upn_sans, /* OUT if non-NULL, a null-terminated array of id-ms-upn-san values found in the certificate are returned */ unsigned char ***kdc_hostname); /* OUT if non-NULL, a null-terminated array of dNSName (hostname) SAN values found in the certificate are returned */ /* * this function checks for acceptable key usage values * in the received certificate. * * when checking a received kdc certificate, it looks for * the kpKdc key usage. if allow_secondary_usage is * non-zero, it will also accept kpServerAuth. * * when checking a received user certificate, it looks for * kpClientAuth key usage. if allow_secondary_usage is * non-zero, it will also accept id-ms-sc-logon EKU. * * this function must also assert that the digitalSignature * key usage is consistent. */ krb5_error_code crypto_check_cert_eku (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ int checking_kdc_cert, /* IN specifies if the received certificate is a KDC certificate (non-zero), or a user certificate (zero) */ int allow_secondary_usage, /* IN specifies if the secondary key usage should be accepted or not (see above) */ int *eku_valid); /* OUT receives non-zero if an acceptable EKU was found */ /* * this functions takes in generated DH secret key and converts * it in to a kerberos session key. it takes into the account the * enc type and then follows the procedure specified in the RFC p 22. */ krb5_error_code pkinit_octetstring2key (krb5_context context, /* IN */ krb5_enctype etype, /* IN specifies the enc type */ unsigned char *key, /* IN contains the DH secret key */ unsigned int key_len, /* IN contains length of key */ krb5_keyblock * krb5key); /* OUT receives kerberos session key */ /* * this function implements clients first part of the DH protocol. * client selects its DH parameters and pub key */ krb5_error_code client_create_dh (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ int dh_size, /* IN specifies the DH modulous, eg 1024, 2048, or 4096 */ unsigned char **dh_paramas, /* OUT contains DER encoded DH params */ unsigned int *dh_params_len, /* OUT contains length of dh_parmas */ unsigned char **dh_pubkey, /* OUT receives DER encoded DH pub key */ unsigned int *dh_pubkey_len); /* OUT receives length of dh_pubkey */ /* * this function completes client's the DH protocol. client * processes received DH pub key from the KDC and computes * the DH secret key */ krb5_error_code client_process_dh (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ unsigned char *dh_pubkey, /* IN contains client's DER encoded DH pub key */ unsigned int dh_pubkey_len, /* IN contains length of dh_pubkey */ unsigned char **dh_session_key, /* OUT receives DH secret key */ unsigned int *dh_session_key_len); /* OUT receives length of dh_session_key */ /* * this function implements the KDC first part of the DH protocol. * it decodes the client's DH parameters and pub key and checks * if they are acceptable. */ krb5_error_code server_check_dh (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ krb5_octet_data *dh_params, /* IN ???? */ int minbits); /* IN the mininum number of key bits acceptable */ /* * this function completes the KDC's DH protocol. The KDC generates * its DH pub key and computes the DH secret key */ krb5_error_code server_process_dh (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ unsigned char *received_pubkey, /* IN contains client's DER encoded DH pub key */ unsigned int received_pub_len, /* IN contains length of received_pubkey */ unsigned char **dh_pubkey, /* OUT receives KDC's DER encoded DH pub key */ unsigned int *dh_pubkey_len, /* OUT receives length of dh_pubkey */ unsigned char **server_key, /* OUT receives DH secret key */ unsigned int *server_key_len); /* OUT receives length of server_key */ /* * this functions takes in crypto specific representation of * supportedCMSTypes and creates a list of * krb5_algorithm_identifier */ krb5_error_code create_krb5_supportedCMSTypes (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ krb5_algorithm_identifier ***supportedCMSTypes); /* OUT */ /* * this functions takes in crypto specific representation of * trustedCertifiers and creates a list of * krb5_external_principal_identifier */ krb5_error_code create_krb5_trustedCertifiers (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ krb5_external_principal_identifier ***trustedCertifiers); /* OUT */ /* * this functions takes in crypto specific representation of * trustedCas (draft9) and creates a list of krb5_trusted_ca (draft9). * draft9 trustedCAs is a CHOICE. we only support choices for * [1] caName and [2] issuerAndSerial. there is no config * option available to select the choice yet. default = 1. */ krb5_error_code create_krb5_trustedCas (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ int flag, /* IN specifies the tag of the CHOICE */ krb5_trusted_ca ***trustedCas); /* OUT */ /* * this functions takes in crypto specific representation of the * KDC's certificate and creates a DER encoded kdcPKId */ krb5_error_code create_issuerAndSerial (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ unsigned char **kdcId_buf, /* OUT receives DER encoded kdcPKId */ unsigned int *kdcId_len); /* OUT receives length of encoded kdcPKId */ /* * process the values from idopts and obtain the cert(s) * specified by those options, populating the id_cryptoctx. */ krb5_error_code crypto_load_certs (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_opts *idopts, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN/OUT */ krb5_principal princ, /* IN */ int do_matching); /* IN */ /* * Free up information held from crypto_load_certs() */ krb5_error_code crypto_free_cert_info (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx); /* * Get number of certificates available after crypto_load_certs() */ krb5_error_code crypto_cert_get_count (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ int *cert_count); /* OUT */ /* * Begin iteration over the certs loaded in crypto_load_certs() */ krb5_error_code crypto_cert_iteration_begin (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ pkinit_cert_iter_handle *iter_handle); /* OUT */ /* * End iteration over the certs loaded in crypto_load_certs() */ krb5_error_code crypto_cert_iteration_end (krb5_context context, /* IN */ pkinit_cert_iter_handle iter_handle); /* IN */ /* * Get next certificate handle */ krb5_error_code crypto_cert_iteration_next (krb5_context context, /* IN */ pkinit_cert_iter_handle iter_handle, /* IN */ pkinit_cert_handle *cert_handle); /* OUT */ /* * Release cert handle */ krb5_error_code crypto_cert_release (krb5_context context, /* IN */ pkinit_cert_handle cert_handle); /* IN */ /* * Get certificate matching information */ krb5_error_code crypto_cert_get_matching_data (krb5_context context, /* IN */ pkinit_cert_handle cert_handle, /* IN */ pkinit_cert_matching_data **ret_data); /* OUT */ /* * Free certificate information */ krb5_error_code crypto_cert_free_matching_data (krb5_context context, /* IN */ pkinit_cert_matching_data *data); /* IN */ /* * Make the given certificate "the chosen one" */ krb5_error_code crypto_cert_select (krb5_context context, /* IN */ pkinit_cert_matching_data *data); /* IN */ /* * Select the default certificate as "the chosen one" */ krb5_error_code crypto_cert_select_default (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx); /* IN */ /* * process the values from idopts and obtain the anchor or * intermediate certificates, or crls specified by idtype, * catype, and id */ krb5_error_code crypto_load_cas_and_crls (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_opts *idopts, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN/OUT */ int idtype, /* IN defines the storage type (file, directory, etc) */ int catype, /* IN defines the ca type (anchor, intermediate, crls) */ char *id); /* IN defines the location (filename, directory name, etc) */ /* * on the client, obtain the kdc's certificate to include * in a request */ krb5_error_code pkinit_get_kdc_cert (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN/OUT */ krb5_principal princ); /* IN */ /* * this function creates edata that contains TD-DH-PARAMETERS */ krb5_error_code pkinit_create_td_dh_parameters (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ pkinit_plg_opts *opts, /* IN */ krb5_data **edata); /* OUT */ /* * this function processes edata that contains TD-DH-PARAMETERS. * the client processes the received acceptable by KDC DH * parameters and picks the first acceptable to it. it matches * them against the known DH parameters. */ krb5_error_code pkinit_process_td_dh_params (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ krb5_algorithm_identifier **algId, /* IN */ int *new_dh_size); /* OUT receives the new DH modulus to use in the new AS-REQ */ /* * this function creates edata that contains TD-INVALID-CERTIFICATES */ krb5_error_code pkinit_create_td_invalid_certificate (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ krb5_data **edata); /* OUT */ /* * this function creates edata that contains TD-TRUSTED-CERTIFIERS */ krb5_error_code pkinit_create_td_trusted_certifiers (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ krb5_data **edata); /* OUT */ /* * this function processes edata that contains either * TD-TRUSTED-CERTIFICATES or TD-INVALID-CERTIFICATES. * current implementation only decodes the received message * but does not act on it */ krb5_error_code pkinit_process_td_trusted_certifiers (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ krb5_external_principal_identifier **trustedCertifiers, /* IN */ int td_type); /* IN */ /* * this function checks if the received kdcPKId matches * the KDC's certificate */ krb5_error_code pkinit_check_kdc_pkid (krb5_context context, /* IN */ pkinit_plg_crypto_context plg_cryptoctx, /* IN */ pkinit_req_crypto_context req_cryptoctx, /* IN */ pkinit_identity_crypto_context id_cryptoctx, /* IN */ unsigned char *pdid_buf, /* IN contains DER encoded kdcPKId */ unsigned int pkid_len, /* IN contains length of pdid_buf */ int *valid_kdcPkId); /* OUT 1 if kdcPKId matches, otherwise 0 */ krb5_error_code pkinit_identity_set_prompter (pkinit_identity_crypto_context id_cryptoctx, /* IN */ krb5_prompter_fct prompter, /* IN */ void *prompter_data); /* IN */ #endif /* _PKINIT_CRYPTO_H */ /* * COPYRIGHT (C) 2006,2007 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN * ALL RIGHTS RESERVED * * Permission is granted to use, copy, create derivative works * and redistribute this software and such derivative works * for any purpose, so long as the name of The University of * Michigan is not used in any advertising or publicity * pertaining to the use of distribution of this software * without specific, written prior authorization. If the * above copyright notice or any other identification of the * University of Michigan is included in any copy of any * portion of this software, then the disclaimer below must * also be included. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF * SUCH DAMAGES. */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, OmniTI Computer Consulting, Inc. All rights reserved. * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. * Copyright 2018 RackTop Systems. */ #include #include #include #include #include #include #include /* Solaris Kerberos */ #include #include #include #include #include "k5-int.h" #include /* * Q: What is this SILLYDECRYPT stuff about? * A: When using the ActivCard Linux pkcs11 library (v2.0.1), * the decrypt function fails. By inserting an extra * function call, which serves nothing but to change the * stack, we were able to work around the issue. If the * ActivCard library is fixed in the future, this * definition and related code can be removed. */ #define SILLYDECRYPT #include "pkinit_crypto_openssl.h" /* * Solaris Kerberos: * Changed to a switch statement so gettext() can be used * for internationization. * Use defined constants rather than raw numbers for error codes. */ static char * pkcs11_error_table(short code) { switch (code) { case CKR_OK: return (gettext("ok")); case CKR_CANCEL: return (gettext("cancel")); case CKR_HOST_MEMORY: return (gettext("host memory")); case CKR_SLOT_ID_INVALID: return (gettext("slot id invalid")); case CKR_GENERAL_ERROR: return (gettext("general error")); case CKR_FUNCTION_FAILED: return (gettext("function failed")); case CKR_ARGUMENTS_BAD: return (gettext("arguments bad")); case CKR_NO_EVENT: return (gettext("no event")); case CKR_NEED_TO_CREATE_THREADS: return (gettext("need to create threads")); case CKR_CANT_LOCK: return (gettext("cant lock")); case CKR_ATTRIBUTE_READ_ONLY: return (gettext("attribute read only")); case CKR_ATTRIBUTE_SENSITIVE: return (gettext("attribute sensitive")); case CKR_ATTRIBUTE_TYPE_INVALID: return (gettext("attribute type invalid")); case CKR_ATTRIBUTE_VALUE_INVALID: return (gettext("attribute value invalid")); case CKR_DATA_INVALID: return (gettext("data invalid")); case CKR_DATA_LEN_RANGE: return (gettext("data len range")); case CKR_DEVICE_ERROR: return (gettext("device error")); case CKR_DEVICE_MEMORY: return (gettext("device memory")); case CKR_DEVICE_REMOVED: return (gettext("device removed")); case CKR_ENCRYPTED_DATA_INVALID: return (gettext("encrypted data invalid")); case CKR_ENCRYPTED_DATA_LEN_RANGE: return (gettext("encrypted data len range")); case CKR_FUNCTION_CANCELED: return (gettext("function canceled")); case CKR_FUNCTION_NOT_PARALLEL: return (gettext("function not parallel")); case CKR_FUNCTION_NOT_SUPPORTED: return (gettext("function not supported")); case CKR_KEY_HANDLE_INVALID: return (gettext("key handle invalid")); case CKR_KEY_SIZE_RANGE: return (gettext("key size range")); case CKR_KEY_TYPE_INCONSISTENT: return (gettext("key type inconsistent")); case CKR_KEY_NOT_NEEDED: return (gettext("key not needed")); case CKR_KEY_CHANGED: return (gettext("key changed")); case CKR_KEY_NEEDED: return (gettext("key needed")); case CKR_KEY_INDIGESTIBLE: return (gettext("key indigestible")); case CKR_KEY_FUNCTION_NOT_PERMITTED: return (gettext("key function not permitted")); case CKR_KEY_NOT_WRAPPABLE: return (gettext("key not wrappable")); case CKR_KEY_UNEXTRACTABLE: return (gettext("key unextractable")); case CKR_MECHANISM_INVALID: return (gettext("mechanism invalid")); case CKR_MECHANISM_PARAM_INVALID: return (gettext("mechanism param invalid")); case CKR_OBJECT_HANDLE_INVALID: return (gettext("object handle invalid")); case CKR_OPERATION_ACTIVE: return (gettext("operation active")); case CKR_OPERATION_NOT_INITIALIZED: return (gettext("operation not initialized")); case CKR_PIN_INCORRECT: return (gettext("pin incorrect")); case CKR_PIN_INVALID: return (gettext("pin invalid")); case CKR_PIN_LEN_RANGE: return (gettext("pin len range")); case CKR_PIN_EXPIRED: return (gettext("pin expired")); case CKR_PIN_LOCKED: return (gettext("pin locked")); case CKR_SESSION_CLOSED: return (gettext("session closed")); case CKR_SESSION_COUNT: return (gettext("session count")); case CKR_SESSION_HANDLE_INVALID: return (gettext("session handle invalid")); case CKR_SESSION_PARALLEL_NOT_SUPPORTED: return (gettext("session parallel not supported")); case CKR_SESSION_READ_ONLY: return (gettext("session read only")); case CKR_SESSION_EXISTS: return (gettext("session exists")); case CKR_SESSION_READ_ONLY_EXISTS: return (gettext("session read only exists")); case CKR_SESSION_READ_WRITE_SO_EXISTS: return (gettext("session read write so exists")); case CKR_SIGNATURE_INVALID: return (gettext("signature invalid")); case CKR_SIGNATURE_LEN_RANGE: return (gettext("signature len range")); case CKR_TEMPLATE_INCOMPLETE: return (gettext("template incomplete")); case CKR_TEMPLATE_INCONSISTENT: return (gettext("template inconsistent")); case CKR_TOKEN_NOT_PRESENT: return (gettext("token not present")); case CKR_TOKEN_NOT_RECOGNIZED: return (gettext("token not recognized")); case CKR_TOKEN_WRITE_PROTECTED: return (gettext("token write protected")); case CKR_UNWRAPPING_KEY_HANDLE_INVALID: return (gettext("unwrapping key handle invalid")); case CKR_UNWRAPPING_KEY_SIZE_RANGE: return (gettext("unwrapping key size range")); case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT: return (gettext("unwrapping key type inconsistent")); case CKR_USER_ALREADY_LOGGED_IN: return (gettext("user already logged in")); case CKR_USER_NOT_LOGGED_IN: return (gettext("user not logged in")); case CKR_USER_PIN_NOT_INITIALIZED: return (gettext("user pin not initialized")); case CKR_USER_TYPE_INVALID: return (gettext("user type invalid")); case CKR_USER_ANOTHER_ALREADY_LOGGED_IN: return (gettext("user another already logged in")); case CKR_USER_TOO_MANY_TYPES: return (gettext("user too many types")); case CKR_WRAPPED_KEY_INVALID: return (gettext("wrapped key invalid")); case CKR_WRAPPED_KEY_LEN_RANGE: return (gettext("wrapped key len range")); case CKR_WRAPPING_KEY_HANDLE_INVALID: return (gettext("wrapping key handle invalid")); case CKR_WRAPPING_KEY_SIZE_RANGE: return (gettext("wrapping key size range")); case CKR_WRAPPING_KEY_TYPE_INCONSISTENT: return (gettext("wrapping key type inconsistent")); case CKR_RANDOM_SEED_NOT_SUPPORTED: return (gettext("random seed not supported")); case CKR_RANDOM_NO_RNG: return (gettext("random no rng")); case CKR_DOMAIN_PARAMS_INVALID: return (gettext("domain params invalid")); case CKR_BUFFER_TOO_SMALL: return (gettext("buffer too small")); case CKR_SAVED_STATE_INVALID: return (gettext("saved state invalid")); case CKR_INFORMATION_SENSITIVE: return (gettext("information sensitive")); case CKR_STATE_UNSAVEABLE: return (gettext("state unsaveable")); case CKR_CRYPTOKI_NOT_INITIALIZED: return (gettext("cryptoki not initialized")); case CKR_CRYPTOKI_ALREADY_INITIALIZED: return (gettext("cryptoki already initialized")); case CKR_MUTEX_BAD: return (gettext("mutex bad")); case CKR_MUTEX_NOT_LOCKED: return (gettext("mutex not locked")); case CKR_FUNCTION_REJECTED: return (gettext("function rejected")); default: return (gettext("unknown error")); } } /* DH parameters */ unsigned char pkinit_1024_dhprime[128] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; unsigned char pkinit_2048_dhprime[2048/8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; unsigned char pkinit_4096_dhprime[4096/8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; #if OPENSSL_VERSION_NUMBER < 0x10100000L /* * Many things have changed in OpenSSL 1.1. The code in this file has been * updated to use the v1.1 APIs but some are new and require emulation * for older OpenSSL versions. */ /* EVP_MD_CTX construct and destructor names have changed */ #define EVP_MD_CTX_new EVP_MD_CTX_create #define EVP_MD_CTX_free EVP_MD_CTX_destroy /* ASN1_STRING_data is deprecated */ #define ASN1_STRING_get0_data ASN1_STRING_data /* X509_STORE_CTX_trusted_stack is deprecated */ #define X509_STORE_CTX_set0_trusted_stack X509_STORE_CTX_trusted_stack /* get_rfc2409_prime_1024() has been renamed. */ #define BN_get_rfc2409_prime_1024 get_rfc2409_prime_1024 #define OBJ_get0_data(o) ((o)->data) #define OBJ_length(o) ((o)->length) /* Some new DH functions that aren't in OpenSSL 1.0.x */ #define DH_bits(dh) BN_num_bits((dh)->p); #define DH_set0_pqg(dh, p, q, g) __DH_set0_pqg(dh, p, q, g) static int __DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) { if ((dh->p == NULL && p == NULL) || (dh->g == NULL && g == NULL)) return 0; if (p != NULL) { BN_free(dh->p); dh->p = p; } if (q != NULL) { BN_free(dh->q); dh->q = q; } if (g != NULL) { BN_free(dh->g); dh->g = g; } if (q != NULL) { dh->length = BN_num_bits(q); } return 1; } #define DH_get0_pqg(dh, p, q, g) __DH_get0_pqg(dh, p, q, g) static void __DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) { if (p != NULL) *p = dh->p; if (q != NULL) *q = dh->q; if (g != NULL) *g = dh->g; } #define DH_set0_key(dh, pub, priv) __DH_set0_key(dh, pub, priv) static int __DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) { if (pub_key != NULL) { BN_free(dh->pub_key); dh->pub_key = pub_key; } if (priv_key != NULL) { BN_free(dh->priv_key); dh->priv_key = priv_key; } return 1; } #define DH_get0_key(dh, pub, priv) __DH_get0_key(dh, pub, priv) static void __DH_get0_key(const DH *dh, const BIGNUM **pub, const BIGNUM **priv) { if (pub != NULL) *pub = dh->pub_key; if (priv != NULL) *priv = dh->priv_key; } #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L || LIBRESSL_VERSION_NUMBER */ krb5_error_code pkinit_init_plg_crypto(pkinit_plg_crypto_context *cryptoctx) { krb5_error_code retval = ENOMEM; pkinit_plg_crypto_context ctx = NULL; /* initialize openssl routines */ /* Solaris Kerberos */ retval = openssl_init(); if (retval != 0) goto out; ctx = (pkinit_plg_crypto_context)malloc(sizeof(*ctx)); if (ctx == NULL) goto out; (void) memset(ctx, 0, sizeof(*ctx)); pkiDebug("%s: initializing openssl crypto context at %p\n", __FUNCTION__, ctx); retval = pkinit_init_pkinit_oids(ctx); if (retval) goto out; retval = pkinit_init_dh_params(ctx); if (retval) goto out; *cryptoctx = ctx; out: if (retval && ctx != NULL) pkinit_fini_plg_crypto(ctx); return retval; } void pkinit_fini_plg_crypto(pkinit_plg_crypto_context cryptoctx) { pkiDebug("%s: freeing context at %p\n", __FUNCTION__, cryptoctx); if (cryptoctx == NULL) return; pkinit_fini_pkinit_oids(cryptoctx); pkinit_fini_dh_params(cryptoctx); free(cryptoctx); } krb5_error_code pkinit_init_identity_crypto(pkinit_identity_crypto_context *idctx) { krb5_error_code retval = ENOMEM; pkinit_identity_crypto_context ctx = NULL; ctx = (pkinit_identity_crypto_context)malloc(sizeof(*ctx)); if (ctx == NULL) goto out; (void) memset(ctx, 0, sizeof(*ctx)); retval = pkinit_init_certs(ctx); if (retval) goto out; retval = pkinit_init_pkcs11(ctx); if (retval) goto out; pkiDebug("%s: returning ctx at %p\n", __FUNCTION__, ctx); *idctx = ctx; out: if (retval) { if (ctx) pkinit_fini_identity_crypto(ctx); } return retval; } void pkinit_fini_identity_crypto(pkinit_identity_crypto_context idctx) { if (idctx == NULL) return; pkiDebug("%s: freeing ctx at %p\n", __FUNCTION__, idctx); pkinit_fini_certs(idctx); pkinit_fini_pkcs11(idctx); free(idctx); } krb5_error_code pkinit_init_req_crypto(pkinit_req_crypto_context *cryptoctx) { pkinit_req_crypto_context ctx = NULL; /* Solaris Kerberos */ if (cryptoctx == NULL) return EINVAL; ctx = (pkinit_req_crypto_context)malloc(sizeof(*ctx)); if (ctx == NULL) return ENOMEM; (void) memset(ctx, 0, sizeof(*ctx)); ctx->dh = NULL; ctx->received_cert = NULL; *cryptoctx = ctx; pkiDebug("%s: returning ctx at %p\n", __FUNCTION__, ctx); return 0; } void pkinit_fini_req_crypto(pkinit_req_crypto_context req_cryptoctx) { if (req_cryptoctx == NULL) return; pkiDebug("%s: freeing ctx at %p\n", __FUNCTION__, req_cryptoctx); if (req_cryptoctx->dh != NULL) DH_free(req_cryptoctx->dh); if (req_cryptoctx->received_cert != NULL) X509_free(req_cryptoctx->received_cert); free(req_cryptoctx); } static krb5_error_code pkinit_init_pkinit_oids(pkinit_plg_crypto_context ctx) { ctx->id_pkinit_san = OBJ_txt2obj("1.3.6.1.5.2.2", 1); if (ctx->id_pkinit_san == NULL) return ENOMEM; ctx->id_pkinit_authData = OBJ_txt2obj("1.3.6.1.5.2.3.1", 1); if (ctx->id_pkinit_authData == NULL) return ENOMEM; ctx->id_pkinit_DHKeyData = OBJ_txt2obj("1.3.6.1.5.2.3.2", 1); if (ctx->id_pkinit_DHKeyData == NULL) return ENOMEM; ctx->id_pkinit_rkeyData = OBJ_txt2obj("1.3.6.1.5.2.3.3", 1); if (ctx->id_pkinit_rkeyData == NULL) return ENOMEM; ctx->id_pkinit_KPClientAuth = OBJ_txt2obj("1.3.6.1.5.2.3.4", 1); if (ctx->id_pkinit_KPClientAuth == NULL) return ENOMEM; ctx->id_pkinit_KPKdc = OBJ_txt2obj("1.3.6.1.5.2.3.5", 1); if (ctx->id_pkinit_KPKdc == NULL) return ENOMEM; ctx->id_ms_kp_sc_logon = OBJ_txt2obj("1.3.6.1.4.1.311.20.2.2", 1); if (ctx->id_ms_kp_sc_logon == NULL) return ENOMEM; ctx->id_ms_san_upn = OBJ_txt2obj("1.3.6.1.4.1.311.20.2.3", 1); if (ctx->id_ms_san_upn == NULL) return ENOMEM; ctx->id_kp_serverAuth = OBJ_txt2obj("1.3.6.1.5.5.7.3.1", 1); if (ctx->id_kp_serverAuth == NULL) return ENOMEM; return 0; } static krb5_error_code get_cert(char *filename, X509 **retcert) { X509 *cert = NULL; BIO *tmp = NULL; int code; krb5_error_code retval; if (filename == NULL || retcert == NULL) return EINVAL; *retcert = NULL; tmp = BIO_new(BIO_s_file()); if (tmp == NULL) return ENOMEM; code = BIO_read_filename(tmp, filename); if (code == 0) { retval = errno; goto cleanup; } cert = (X509 *) PEM_read_bio_X509(tmp, NULL, NULL, NULL); if (cert == NULL) { retval = EIO; pkiDebug("failed to read certificate from %s\n", filename); goto cleanup; } *retcert = cert; retval = 0; cleanup: if (tmp != NULL) BIO_free(tmp); return retval; } static krb5_error_code get_key(char *filename, EVP_PKEY **retkey) { EVP_PKEY *pkey = NULL; BIO *tmp = NULL; int code; krb5_error_code retval; if (filename == NULL || retkey == NULL) return EINVAL; tmp = BIO_new(BIO_s_file()); if (tmp == NULL) return ENOMEM; code = BIO_read_filename(tmp, filename); if (code == 0) { retval = errno; goto cleanup; } pkey = (EVP_PKEY *) PEM_read_bio_PrivateKey(tmp, NULL, NULL, NULL); if (pkey == NULL) { retval = EIO; pkiDebug("failed to read private key from %s\n", filename); goto cleanup; } *retkey = pkey; retval = 0; cleanup: if (tmp != NULL) BIO_free(tmp); return retval; } static void pkinit_fini_pkinit_oids(pkinit_plg_crypto_context ctx) { if (ctx == NULL) return; ASN1_OBJECT_free(ctx->id_pkinit_san); ASN1_OBJECT_free(ctx->id_pkinit_authData); ASN1_OBJECT_free(ctx->id_pkinit_DHKeyData); ASN1_OBJECT_free(ctx->id_pkinit_rkeyData); ASN1_OBJECT_free(ctx->id_pkinit_KPClientAuth); ASN1_OBJECT_free(ctx->id_pkinit_KPKdc); ASN1_OBJECT_free(ctx->id_ms_kp_sc_logon); ASN1_OBJECT_free(ctx->id_ms_san_upn); ASN1_OBJECT_free(ctx->id_kp_serverAuth); } static DH * make_dhprime(uint8_t *prime, size_t len) { DH *dh = NULL; BIGNUM *p = NULL, *q = NULL, *g = NULL; if ((p = BN_bin2bn(prime, len, NULL)) == NULL) goto cleanup; if ((q = BN_new()) == NULL) goto cleanup; if (!BN_rshift1(q, p)) goto cleanup; if ((g = BN_new()) == NULL) goto cleanup; if (!BN_set_word(g, DH_GENERATOR_2)) goto cleanup; dh = DH_new(); if (dh == NULL) goto cleanup; DH_set0_pqg(dh, p, q, g); p = g = q = NULL; cleanup: BN_free(p); BN_free(q); BN_free(g); return dh; } static krb5_error_code pkinit_init_dh_params(pkinit_plg_crypto_context plgctx) { krb5_error_code retval = ENOMEM; plgctx->dh_1024 = make_dhprime(pkinit_1024_dhprime, sizeof(pkinit_1024_dhprime)); if (plgctx->dh_1024 == NULL) goto cleanup; plgctx->dh_2048 = make_dhprime(pkinit_2048_dhprime, sizeof(pkinit_2048_dhprime)); if (plgctx->dh_2048 == NULL) goto cleanup; plgctx->dh_4096 = make_dhprime(pkinit_4096_dhprime, sizeof(pkinit_4096_dhprime)); if (plgctx->dh_4096 == NULL) goto cleanup; retval = 0; cleanup: if (retval) pkinit_fini_dh_params(plgctx); return retval; } static void pkinit_fini_dh_params(pkinit_plg_crypto_context plgctx) { if (plgctx->dh_1024 != NULL) DH_free(plgctx->dh_1024); if (plgctx->dh_2048 != NULL) DH_free(plgctx->dh_2048); if (plgctx->dh_4096 != NULL) DH_free(plgctx->dh_4096); plgctx->dh_1024 = plgctx->dh_2048 = plgctx->dh_4096 = NULL; } static krb5_error_code pkinit_init_certs(pkinit_identity_crypto_context ctx) { /* Solaris Kerberos */ int i; for (i = 0; i < MAX_CREDS_ALLOWED; i++) ctx->creds[i] = NULL; ctx->my_certs = NULL; ctx->cert_index = 0; ctx->my_key = NULL; ctx->trustedCAs = NULL; ctx->intermediateCAs = NULL; ctx->revoked = NULL; return 0; } static void pkinit_fini_certs(pkinit_identity_crypto_context ctx) { if (ctx == NULL) return; if (ctx->my_certs != NULL) sk_X509_pop_free(ctx->my_certs, X509_free); if (ctx->my_key != NULL) EVP_PKEY_free(ctx->my_key); if (ctx->trustedCAs != NULL) sk_X509_pop_free(ctx->trustedCAs, X509_free); if (ctx->intermediateCAs != NULL) sk_X509_pop_free(ctx->intermediateCAs, X509_free); if (ctx->revoked != NULL) sk_X509_CRL_pop_free(ctx->revoked, X509_CRL_free); } static krb5_error_code pkinit_init_pkcs11(pkinit_identity_crypto_context ctx) { /* Solaris Kerberos */ #ifndef WITHOUT_PKCS11 ctx->p11_module_name = strdup(PKCS11_MODNAME); if (ctx->p11_module_name == NULL) return ENOMEM; ctx->p11_module = NULL; ctx->slotid = PK_NOSLOT; ctx->token_label = NULL; ctx->cert_label = NULL; ctx->PIN = NULL; ctx->session = CK_INVALID_HANDLE; ctx->p11 = NULL; ctx->p11flags = 0; /* Solaris Kerberos */ #endif ctx->pkcs11_method = 0; (void) memset(ctx->creds, 0, sizeof(ctx->creds)); return 0; } static void pkinit_fini_pkcs11(pkinit_identity_crypto_context ctx) { #ifndef WITHOUT_PKCS11 if (ctx == NULL) return; if (ctx->p11 != NULL) { if (ctx->session != CK_INVALID_HANDLE) { ctx->p11->C_CloseSession(ctx->session); ctx->session = CK_INVALID_HANDLE; } /* * Solaris Kerberos: * Only call C_Finalize if the process was not already using pkcs11. */ if (ctx->finalize_pkcs11 == TRUE) ctx->p11->C_Finalize(NULL_PTR); ctx->p11 = NULL; } if (ctx->p11_module != NULL) { pkinit_C_UnloadModule(ctx->p11_module); ctx->p11_module = NULL; } if (ctx->p11_module_name != NULL) free(ctx->p11_module_name); if (ctx->token_label != NULL) free(ctx->token_label); if (ctx->cert_id != NULL) free(ctx->cert_id); if (ctx->cert_label != NULL) free(ctx->cert_label); if (ctx->PIN != NULL) { (void) memset(ctx->PIN, 0, strlen(ctx->PIN)); free(ctx->PIN); } #endif } krb5_error_code pkinit_identity_set_prompter(pkinit_identity_crypto_context id_cryptoctx, krb5_prompter_fct prompter, void *prompter_data) { id_cryptoctx->prompter = prompter; id_cryptoctx->prompter_data = prompter_data; return 0; } /* Create a CMS ContentInfo of type oid containing the octet string in data. */ static krb5_error_code create_contentinfo(krb5_context context, ASN1_OBJECT *oid, unsigned char *data, size_t data_len, PKCS7 **p7_out) { PKCS7 *p7 = NULL; ASN1_OCTET_STRING *ostr = NULL; *p7_out = NULL; ostr = ASN1_OCTET_STRING_new(); if (ostr == NULL) goto oom; if (!ASN1_OCTET_STRING_set(ostr, (unsigned char *)data, data_len)) goto oom; p7 = PKCS7_new(); if (p7 == NULL) goto oom; p7->type = OBJ_dup(oid); if (p7->type == NULL) goto oom; if (OBJ_obj2nid(oid) == NID_pkcs7_data) { /* Draft 9 uses id-pkcs7-data for signed data. For this type OpenSSL * expects an octet string in d.data. */ p7->d.data = ostr; } else { p7->d.other = ASN1_TYPE_new(); if (p7->d.other == NULL) goto oom; p7->d.other->type = V_ASN1_OCTET_STRING; p7->d.other->value.octet_string = ostr; } *p7_out = p7; return 0; oom: if (ostr != NULL) ASN1_OCTET_STRING_free(ostr); if (p7 != NULL) PKCS7_free(p7); return ENOMEM; } /* ARGSUSED */ krb5_error_code cms_signeddata_create(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, int cms_msg_type, int include_certchain, unsigned char *data, unsigned int data_len, unsigned char **signed_data, unsigned int *signed_data_len) { /* Solaris Kerberos */ krb5_error_code retval = KRB5KRB_ERR_GENERIC; PKCS7 *p7 = NULL, *inner_p7 = NULL; PKCS7_SIGNED *p7s = NULL; PKCS7_SIGNER_INFO *p7si = NULL; unsigned char *p; STACK_OF(X509) * cert_stack = NULL; ASN1_OCTET_STRING *digest_attr = NULL; EVP_MD_CTX *ctx = NULL, *ctx2 = NULL; const EVP_MD *md_tmp = NULL; unsigned char md_data[EVP_MAX_MD_SIZE], md_data2[EVP_MAX_MD_SIZE]; unsigned char *digestInfo_buf = NULL, *abuf = NULL; unsigned int md_len, md_len2, alen, digestInfo_len; STACK_OF(X509_ATTRIBUTE) * sk; unsigned char *sig = NULL; unsigned int sig_len = 0; X509_ALGOR *alg = NULL; ASN1_OCTET_STRING *digest = NULL; unsigned int alg_len = 0, digest_len = 0; unsigned char *y = NULL, *alg_buf = NULL, *digest_buf = NULL; X509 *cert = NULL; ASN1_OBJECT *oid = NULL, *oid_copy; /* Solaris Kerberos */ if (signed_data == NULL) return EINVAL; if (signed_data_len == NULL) return EINVAL; /* start creating PKCS7 data */ if ((p7 = PKCS7_new()) == NULL) goto cleanup; p7->type = OBJ_nid2obj(NID_pkcs7_signed); if ((p7s = PKCS7_SIGNED_new()) == NULL) goto cleanup; p7->d.sign = p7s; if (!ASN1_INTEGER_set(p7s->version, 3)) goto cleanup; /* create a cert chain that has at least the signer's certificate */ if ((cert_stack = sk_X509_new_null()) == NULL) goto cleanup; cert = sk_X509_value(id_cryptoctx->my_certs, id_cryptoctx->cert_index); if (!include_certchain) { pkiDebug("only including signer's certificate\n"); sk_X509_push(cert_stack, X509_dup(cert)); } else { /* create a cert chain */ X509_STORE *certstore = NULL; X509_STORE_CTX *certctx; STACK_OF(X509) *certstack = NULL; char buf[DN_BUF_LEN]; int i = 0, size = 0; if ((certstore = X509_STORE_new()) == NULL) goto cleanup; if ((certctx = X509_STORE_CTX_new()) == NULL) goto cleanup; pkiDebug("building certificate chain\n"); X509_STORE_set_verify_cb(certstore, openssl_callback); X509_STORE_CTX_init(certctx, certstore, cert, id_cryptoctx->intermediateCAs); X509_STORE_CTX_set0_trusted_stack(certctx, id_cryptoctx->trustedCAs); /* Solaris Kerberos */ if (X509_verify_cert(certctx) <= 0) { pkiDebug("failed to create a certificate chain: %s\n", X509_verify_cert_error_string(X509_STORE_CTX_get_error(certctx))); if (!sk_X509_num(id_cryptoctx->trustedCAs)) pkiDebug("No trusted CAs found. Check your X509_anchors\n"); goto cleanup; } certstack = X509_STORE_CTX_get1_chain(certctx); size = sk_X509_num(certstack); pkiDebug("size of certificate chain = %d\n", size); for(i = 0; i < size - 1; i++) { X509 *x = sk_X509_value(certstack, i); X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf)); pkiDebug("cert #%d: %s\n", i, buf); sk_X509_push(cert_stack, X509_dup(x)); } X509_STORE_CTX_free(certctx); X509_STORE_free(certstore); sk_X509_pop_free(certstack, X509_free); } p7s->cert = cert_stack; /* fill-in PKCS7_SIGNER_INFO */ if ((p7si = PKCS7_SIGNER_INFO_new()) == NULL) goto cleanup; if (!ASN1_INTEGER_set(p7si->version, 1)) goto cleanup; if (!X509_NAME_set(&p7si->issuer_and_serial->issuer, X509_get_issuer_name(cert))) goto cleanup; /* because ASN1_INTEGER_set is used to set a 'long' we will do * things the ugly way. */ ASN1_INTEGER_free(p7si->issuer_and_serial->serial); if (!(p7si->issuer_and_serial->serial = ASN1_INTEGER_dup(X509_get_serialNumber(cert)))) goto cleanup; /* will not fill-out EVP_PKEY because it's on the smartcard */ /* Set digest algs */ p7si->digest_alg->algorithm = OBJ_nid2obj(NID_sha1); if (p7si->digest_alg->parameter != NULL) ASN1_TYPE_free(p7si->digest_alg->parameter); if ((p7si->digest_alg->parameter = ASN1_TYPE_new()) == NULL) goto cleanup; p7si->digest_alg->parameter->type = V_ASN1_NULL; /* Set sig algs */ if (p7si->digest_enc_alg->parameter != NULL) ASN1_TYPE_free(p7si->digest_enc_alg->parameter); p7si->digest_enc_alg->algorithm = OBJ_nid2obj(NID_sha1WithRSAEncryption); if (!(p7si->digest_enc_alg->parameter = ASN1_TYPE_new())) goto cleanup; p7si->digest_enc_alg->parameter->type = V_ASN1_NULL; /* pick the correct oid for the eContentInfo */ oid = pkinit_pkcs7type2oid(plg_cryptoctx, cms_msg_type); if (oid == NULL) goto cleanup; if (cms_msg_type == CMS_SIGN_DRAFT9) { /* don't include signed attributes for pa-type 15 request */ abuf = data; alen = data_len; } else { /* add signed attributes */ /* compute sha1 digest over the EncapsulatedContentInfo */ ctx = EVP_MD_CTX_new(); if (ctx == NULL) goto cleanup2; EVP_MD_CTX_init(ctx); EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); EVP_DigestUpdate(ctx, data, data_len); md_tmp = EVP_MD_CTX_md(ctx); EVP_DigestFinal_ex(ctx, md_data, &md_len); EVP_MD_CTX_free(ctx); ctx = NULL; /* create a message digest attr */ digest_attr = ASN1_OCTET_STRING_new(); ASN1_OCTET_STRING_set(digest_attr, md_data, (int)md_len); PKCS7_add_signed_attribute(p7si, NID_pkcs9_messageDigest, V_ASN1_OCTET_STRING, (char *) digest_attr); /* create a content-type attr */ oid_copy = OBJ_dup(oid); if (oid_copy == NULL) goto cleanup2; PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType, V_ASN1_OBJECT, oid_copy); /* create the signature over signed attributes. get DER encoded value */ /* This is the place where smartcard signature needs to be calculated */ sk = p7si->auth_attr; alen = ASN1_item_i2d((ASN1_VALUE *) sk, &abuf, ASN1_ITEM_rptr(PKCS7_ATTR_SIGN)); if (abuf == NULL) goto cleanup2; } #ifndef WITHOUT_PKCS11 /* Some tokens can only do RSAEncryption without sha1 hash */ /* to compute sha1WithRSAEncryption, encode the algorithm ID for the hash * function and the hash value into an ASN.1 value of type DigestInfo * DigestInfo::=SEQUENCE { * digestAlgorithm AlgorithmIdentifier, * digest OCTET STRING } */ if (id_cryptoctx->pkcs11_method == 1 && id_cryptoctx->mech == CKM_RSA_PKCS) { pkiDebug("mech = CKM_RSA_PKCS\n"); ctx2 = EVP_MD_CTX_new(); if (ctx2 == NULL) goto cleanup2; EVP_MD_CTX_init(ctx2); /* if this is not draft9 request, include digest signed attribute */ if (cms_msg_type != CMS_SIGN_DRAFT9) EVP_DigestInit_ex(ctx2, md_tmp, NULL); else EVP_DigestInit_ex(ctx2, EVP_sha1(), NULL); EVP_DigestUpdate(ctx2, abuf, alen); EVP_DigestFinal_ex(ctx2, md_data2, &md_len2); EVP_MD_CTX_free(ctx2); ctx2 = NULL; alg = X509_ALGOR_new(); if (alg == NULL) goto cleanup2; alg->algorithm = OBJ_nid2obj(NID_sha1); alg->parameter = NULL; alg_len = i2d_X509_ALGOR(alg, NULL); alg_buf = (unsigned char *)malloc(alg_len); if (alg_buf == NULL) goto cleanup2; digest = ASN1_OCTET_STRING_new(); if (digest == NULL) goto cleanup2; ASN1_OCTET_STRING_set(digest, md_data2, (int)md_len2); digest_len = i2d_ASN1_OCTET_STRING(digest, NULL); digest_buf = (unsigned char *)malloc(digest_len); if (digest_buf == NULL) goto cleanup2; digestInfo_len = ASN1_object_size(1, (int)(alg_len + digest_len), V_ASN1_SEQUENCE); y = digestInfo_buf = (unsigned char *)malloc(digestInfo_len); if (digestInfo_buf == NULL) goto cleanup2; ASN1_put_object(&y, 1, (int)(alg_len + digest_len), V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL); i2d_X509_ALGOR(alg, &y); i2d_ASN1_OCTET_STRING(digest, &y); #ifdef DEBUG_SIG pkiDebug("signing buffer\n"); print_buffer(digestInfo_buf, digestInfo_len); print_buffer_bin(digestInfo_buf, digestInfo_len, "/tmp/pkcs7_tosign"); #endif retval = pkinit_sign_data(context, id_cryptoctx, digestInfo_buf, digestInfo_len, &sig, &sig_len); } else #endif { pkiDebug("mech = %s\n", id_cryptoctx->pkcs11_method == 1 ? "CKM_SHA1_RSA_PKCS" : "FS"); retval = pkinit_sign_data(context, id_cryptoctx, abuf, alen, &sig, &sig_len); } #ifdef DEBUG_SIG print_buffer(sig, sig_len); #endif if (cms_msg_type != CMS_SIGN_DRAFT9) free(abuf); if (retval) goto cleanup2; /* Add signature */ if (!ASN1_STRING_set(p7si->enc_digest, (unsigned char *) sig, (int)sig_len)) { unsigned long err = ERR_peek_error(); retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, retval, "%s\n", ERR_error_string(err, NULL)); pkiDebug("failed to add a signed digest attribute\n"); goto cleanup2; } /* adder signer_info to pkcs7 signed */ if (!PKCS7_add_signer(p7, p7si)) goto cleanup2; /* start on adding data to the pkcs7 signed */ retval = create_contentinfo(context, oid, data, data_len, &inner_p7); if (p7s->contents != NULL) PKCS7_free(p7s->contents); p7s->contents = inner_p7; *signed_data_len = i2d_PKCS7(p7, NULL); if (!(*signed_data_len)) { unsigned long err = ERR_peek_error(); retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, retval, "%s\n", ERR_error_string(err, NULL)); pkiDebug("failed to der encode pkcs7\n"); goto cleanup2; } if ((p = *signed_data = (unsigned char *) malloc((size_t)*signed_data_len)) == NULL) goto cleanup2; /* DER encode PKCS7 data */ retval = i2d_PKCS7(p7, &p); if (!retval) { unsigned long err = ERR_peek_error(); retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, retval, "%s\n", ERR_error_string(err, NULL)); pkiDebug("failed to der encode pkcs7\n"); goto cleanup2; } retval = 0; #ifdef DEBUG_ASN1 if (cms_msg_type == CMS_SIGN_CLIENT) { print_buffer_bin(*signed_data, *signed_data_len, "/tmp/client_pkcs7_signeddata"); } else { if (cms_msg_type == CMS_SIGN_SERVER) { print_buffer_bin(*signed_data, *signed_data_len, "/tmp/kdc_pkcs7_signeddata"); } else { print_buffer_bin(*signed_data, *signed_data_len, "/tmp/draft9_pkcs7_signeddata"); } } #endif cleanup2: if (cms_msg_type != CMS_SIGN_DRAFT9) if (ctx != NULL) EVP_MD_CTX_free(ctx); #ifndef WITHOUT_PKCS11 if (id_cryptoctx->pkcs11_method == 1 && id_cryptoctx->mech == CKM_RSA_PKCS) { if (ctx2 != NULL) EVP_MD_CTX_free(ctx2); if (digest_buf != NULL) free(digest_buf); if (digestInfo_buf != NULL) free(digestInfo_buf); if (alg_buf != NULL) free(alg_buf); if (digest != NULL) ASN1_OCTET_STRING_free(digest); } #endif if (alg != NULL) X509_ALGOR_free(alg); cleanup: if (p7 != NULL) PKCS7_free(p7); if (sig != NULL) free(sig); return retval; } krb5_error_code cms_signeddata_verify(krb5_context context, pkinit_plg_crypto_context plgctx, pkinit_req_crypto_context reqctx, pkinit_identity_crypto_context idctx, int cms_msg_type, int require_crl_checking, unsigned char *signed_data, unsigned int signed_data_len, unsigned char **data, unsigned int *data_len, unsigned char **authz_data, unsigned int *authz_data_len) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; PKCS7 *p7 = NULL; BIO *out = NULL; int flags = PKCS7_NOVERIFY, i = 0; unsigned int vflags = 0, size = 0; const unsigned char *p = signed_data; STACK_OF(PKCS7_SIGNER_INFO) *si_sk = NULL; PKCS7_SIGNER_INFO *si = NULL; X509 *x = NULL; X509_STORE *store = NULL; X509_STORE_CTX *cert_ctx; STACK_OF(X509) *intermediateCAs = NULL; STACK_OF(X509_CRL) *revoked = NULL; STACK_OF(X509) *verified_chain = NULL; ASN1_OBJECT *oid = NULL; krb5_external_principal_identifier **krb5_verified_chain = NULL; krb5_data *authz = NULL; char buf[DN_BUF_LEN]; #ifdef DEBUG_ASN1 print_buffer_bin(signed_data, signed_data_len, "/tmp/client_received_pkcs7_signeddata"); #endif oid = pkinit_pkcs7type2oid(plgctx, cms_msg_type); if (oid == NULL) goto cleanup; /* decode received PKCS7 message */ if ((p7 = d2i_PKCS7(NULL, &p, (int)signed_data_len)) == NULL) { unsigned long err = ERR_peek_error(); krb5_set_error_message(context, retval, "%s\n", ERR_error_string(err, NULL)); pkiDebug("%s: failed to decode message: %s\n", __FUNCTION__, ERR_error_string(err, NULL)); goto cleanup; } /* verify that the received message is PKCS7 SignedData message */ if (OBJ_obj2nid(p7->type) != NID_pkcs7_signed) { pkiDebug("Expected id-signedData PKCS7 msg (received type = %d)\n", OBJ_obj2nid(p7->type)); krb5_set_error_message(context, retval, "wrong oid\n"); goto cleanup; } /* setup to verify X509 certificate used to sign PKCS7 message */ if (!(store = X509_STORE_new())) goto cleanup; /* check if we are inforcing CRL checking */ vflags = X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL; if (require_crl_checking) X509_STORE_set_verify_cb(store, openssl_callback); else X509_STORE_set_verify_cb(store, openssl_callback_ignore_crls); X509_STORE_set_flags(store, vflags); /* get the signer's information from the PKCS7 message */ if ((si_sk = PKCS7_get_signer_info(p7)) == NULL) goto cleanup; if ((si = sk_PKCS7_SIGNER_INFO_value(si_sk, 0)) == NULL) goto cleanup; if ((x = PKCS7_cert_from_signer_info(p7, si)) == NULL) goto cleanup; /* create available CRL information (get local CRLs and include CRLs * received in the PKCS7 message */ if (idctx->revoked == NULL) revoked = p7->d.sign->crl; else if (p7->d.sign->crl == NULL) revoked = idctx->revoked; else { size = sk_X509_CRL_num(idctx->revoked); revoked = sk_X509_CRL_new_null(); for (i = 0; i < size; i++) sk_X509_CRL_push(revoked, sk_X509_CRL_value(idctx->revoked, i)); size = sk_X509_CRL_num(p7->d.sign->crl); for (i = 0; i < size; i++) sk_X509_CRL_push(revoked, sk_X509_CRL_value(p7->d.sign->crl, i)); } /* create available intermediate CAs chains (get local intermediateCAs and * include the CA chain received in the PKCS7 message */ if (idctx->intermediateCAs == NULL) intermediateCAs = p7->d.sign->cert; else if (p7->d.sign->cert == NULL) intermediateCAs = idctx->intermediateCAs; else { size = sk_X509_num(idctx->intermediateCAs); intermediateCAs = sk_X509_new_null(); for (i = 0; i < size; i++) { sk_X509_push(intermediateCAs, sk_X509_value(idctx->intermediateCAs, i)); } size = sk_X509_num(p7->d.sign->cert); for (i = 0; i < size; i++) { sk_X509_push(intermediateCAs, sk_X509_value(p7->d.sign->cert, i)); } } /* initialize x509 context with the received certificate and * trusted and intermediate CA chains and CRLs */ if ((cert_ctx = X509_STORE_CTX_new()) == NULL) goto cleanup; if (!X509_STORE_CTX_init(cert_ctx, store, x, intermediateCAs)) goto cleanup; X509_STORE_CTX_set0_crls(cert_ctx, revoked); /* add trusted CAs certificates for cert verification */ if (idctx->trustedCAs != NULL) X509_STORE_CTX_set0_trusted_stack(cert_ctx, idctx->trustedCAs); else { pkiDebug("unable to find any trusted CAs\n"); goto cleanup; } #ifdef DEBUG_CERTCHAIN if (intermediateCAs != NULL) { size = sk_X509_num(intermediateCAs); pkiDebug("untrusted cert chain of size %d\n", size); for (i = 0; i < size; i++) { X509_NAME_oneline(X509_get_subject_name( sk_X509_value(intermediateCAs, i)), buf, sizeof(buf)); pkiDebug("cert #%d: %s\n", i, buf); } } if (idctx->trustedCAs != NULL) { size = sk_X509_num(idctx->trustedCAs); pkiDebug("trusted cert chain of size %d\n", size); for (i = 0; i < size; i++) { X509_NAME_oneline(X509_get_subject_name( sk_X509_value(idctx->trustedCAs, i)), buf, sizeof(buf)); pkiDebug("cert #%d: %s\n", i, buf); } } if (revoked != NULL) { size = sk_X509_CRL_num(revoked); pkiDebug("CRL chain of size %d\n", size); for (i = 0; i < size; i++) { X509_CRL *crl = sk_X509_CRL_value(revoked, i); X509_NAME_oneline(X509_CRL_get_issuer(crl), buf, sizeof(buf)); pkiDebug("crls by CA #%d: %s\n", i , buf); } } #endif i = X509_verify_cert(cert_ctx); if (i <= 0) { int j = X509_STORE_CTX_get_error(cert_ctx); reqctx->received_cert = X509_dup( X509_STORE_CTX_get_current_cert(cert_ctx)); switch(j) { case X509_V_ERR_CERT_REVOKED: retval = KRB5KDC_ERR_REVOKED_CERTIFICATE; break; case X509_V_ERR_UNABLE_TO_GET_CRL: retval = KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN; break; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: retval = KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE; break; default: retval = KRB5KDC_ERR_INVALID_CERTIFICATE; } X509_NAME_oneline(X509_get_subject_name( reqctx->received_cert), buf, sizeof(buf)); pkiDebug("problem with cert DN = %s (error=%d) %s\n", buf, j, X509_verify_cert_error_string(j)); krb5_set_error_message(context, retval, "%s\n", X509_verify_cert_error_string(j)); #ifdef DEBUG_CERTCHAIN size = sk_X509_num(p7->d.sign->cert); pkiDebug("received cert chain of size %d\n", size); for (j = 0; j < size; j++) { X509 *tmp_cert = sk_X509_value(p7->d.sign->cert, j); X509_NAME_oneline(X509_get_subject_name(tmp_cert), buf, sizeof(buf)); pkiDebug("cert #%d: %s\n", j, buf); } #endif } else { /* retrieve verified certificate chain */ if (cms_msg_type == CMS_SIGN_CLIENT || cms_msg_type == CMS_SIGN_DRAFT9) verified_chain = X509_STORE_CTX_get1_chain(cert_ctx); } X509_STORE_CTX_free(cert_ctx); if (i <= 0) goto cleanup; out = BIO_new(BIO_s_mem()); if (cms_msg_type == CMS_SIGN_DRAFT9) flags |= PKCS7_NOATTR; if (PKCS7_verify(p7, NULL, store, NULL, out, flags)) { int valid_oid = 0; if (!OBJ_cmp(p7->d.sign->contents->type, oid)) valid_oid = 1; else if (cms_msg_type == CMS_SIGN_DRAFT9) { /* * Various implementations of the pa-type 15 request use * different OIDS. We check that the returned object * has any of the acceptable OIDs */ ASN1_OBJECT *client_oid = NULL, *server_oid = NULL, *rsa_oid = NULL; client_oid = pkinit_pkcs7type2oid(plgctx, CMS_SIGN_CLIENT); server_oid = pkinit_pkcs7type2oid(plgctx, CMS_SIGN_SERVER); rsa_oid = pkinit_pkcs7type2oid(plgctx, CMS_ENVEL_SERVER); if (!OBJ_cmp(p7->d.sign->contents->type, client_oid) || !OBJ_cmp(p7->d.sign->contents->type, server_oid) || !OBJ_cmp(p7->d.sign->contents->type, rsa_oid)) valid_oid = 1; } if (valid_oid) pkiDebug("PKCS7 Verification successful\n"); else { const ASN1_OBJECT *etype = p7->d.sign->contents->type; pkiDebug("wrong oid in eContentType\n"); print_buffer((unsigned char *)OBJ_get0_data(etype), OBJ_length(etype)); retval = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, retval, "wrong oid\n"); goto cleanup; } } else { unsigned long err = ERR_peek_error(); switch(ERR_GET_REASON(err)) { case PKCS7_R_DIGEST_FAILURE: retval = KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED; break; case PKCS7_R_SIGNATURE_FAILURE: default: retval = KRB5KDC_ERR_INVALID_SIG; } pkiDebug("PKCS7 Verification failure\n"); krb5_set_error_message(context, retval, "%s\n", ERR_error_string(err, NULL)); goto cleanup; } /* transfer the data from PKCS7 message into return buffer */ for (size = 0;;) { if ((*data = realloc(*data, size + 1024 * 10)) == NULL) goto cleanup; i = BIO_read(out, &((*data)[size]), 1024 * 10); if (i <= 0) break; else size += i; } *data_len = size; reqctx->received_cert = X509_dup(x); /* generate authorization data */ if (cms_msg_type == CMS_SIGN_CLIENT || cms_msg_type == CMS_SIGN_DRAFT9) { if (authz_data == NULL || authz_data_len == NULL) goto out; *authz_data = NULL; retval = create_identifiers_from_stack(verified_chain, &krb5_verified_chain); if (retval) { pkiDebug("create_identifiers_from_stack failed\n"); goto cleanup; } retval = k5int_encode_krb5_td_trusted_certifiers((const krb5_external_principal_identifier **)krb5_verified_chain, &authz); if (retval) { pkiDebug("encode_krb5_td_trusted_certifiers failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)authz->data, authz->length, "/tmp/kdc_ad_initial_verified_cas"); #endif *authz_data = (unsigned char *)malloc(authz->length); if (*authz_data == NULL) { retval = ENOMEM; goto cleanup; } (void) memcpy(*authz_data, authz->data, authz->length); *authz_data_len = authz->length; } out: retval = 0; cleanup: if (out != NULL) BIO_free(out); if (store != NULL) X509_STORE_free(store); if (p7 != NULL) { if (idctx->intermediateCAs != NULL && p7->d.sign->cert) sk_X509_free(intermediateCAs); if (idctx->revoked != NULL && p7->d.sign->crl) sk_X509_CRL_free(revoked); PKCS7_free(p7); } if (verified_chain != NULL) sk_X509_pop_free(verified_chain, X509_free); if (krb5_verified_chain != NULL) free_krb5_external_principal_identifier(&krb5_verified_chain); if (authz != NULL) krb5_free_data(context, authz); return retval; } krb5_error_code cms_envelopeddata_create(krb5_context context, pkinit_plg_crypto_context plgctx, pkinit_req_crypto_context reqctx, pkinit_identity_crypto_context idctx, krb5_preauthtype pa_type, int include_certchain, unsigned char *key_pack, unsigned int key_pack_len, unsigned char **out, unsigned int *out_len) { /* Solaris Kerberos */ krb5_error_code retval = KRB5KRB_ERR_GENERIC; PKCS7 *p7 = NULL; BIO *in = NULL; unsigned char *p = NULL, *signed_data = NULL, *enc_data = NULL; int signed_data_len = 0, enc_data_len = 0, flags = PKCS7_BINARY; STACK_OF(X509) *encerts = NULL; const EVP_CIPHER *cipher = NULL; int cms_msg_type; /* create the PKCS7 SignedData portion of the PKCS7 EnvelopedData */ switch ((int)pa_type) { case KRB5_PADATA_PK_AS_REQ_OLD: case KRB5_PADATA_PK_AS_REP_OLD: cms_msg_type = CMS_SIGN_DRAFT9; break; case KRB5_PADATA_PK_AS_REQ: cms_msg_type = CMS_ENVEL_SERVER; break; default: /* Solaris Kerberos */ retval = EINVAL; goto cleanup; } retval = cms_signeddata_create(context, plgctx, reqctx, idctx, cms_msg_type, include_certchain, key_pack, key_pack_len, &signed_data, (unsigned int *)&signed_data_len); if (retval) { pkiDebug("failed to create pkcs7 signed data\n"); goto cleanup; } /* check we have client's certificate */ if (reqctx->received_cert == NULL) { retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } encerts = sk_X509_new_null(); sk_X509_push(encerts, reqctx->received_cert); cipher = EVP_des_ede3_cbc(); in = BIO_new(BIO_s_mem()); switch (pa_type) { case KRB5_PADATA_PK_AS_REQ: prepare_enc_data(signed_data, signed_data_len, &enc_data, &enc_data_len); retval = BIO_write(in, enc_data, enc_data_len); if (retval != enc_data_len) { pkiDebug("BIO_write only wrote %d\n", retval); goto cleanup; } break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: retval = BIO_write(in, signed_data, signed_data_len); if (retval != signed_data_len) { pkiDebug("BIO_write only wrote %d\n", retval); /* Solaris Kerberos */ retval = KRB5KRB_ERR_GENERIC; goto cleanup; } break; default: retval = -1; goto cleanup; } p7 = PKCS7_encrypt(encerts, in, cipher, flags); if (p7 == NULL) { pkiDebug("failed to encrypt PKCS7 object\n"); retval = -1; goto cleanup; } switch (pa_type) { case KRB5_PADATA_PK_AS_REQ: p7->d.enveloped->enc_data->content_type = OBJ_nid2obj(NID_pkcs7_signed); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: p7->d.enveloped->enc_data->content_type = OBJ_nid2obj(NID_pkcs7_data); break; } *out_len = i2d_PKCS7(p7, NULL); if (!*out_len || (p = *out = (unsigned char *)malloc(*out_len)) == NULL) { retval = ENOMEM; goto cleanup; } retval = i2d_PKCS7(p7, &p); if (!retval) { pkiDebug("unable to write pkcs7 object\n"); goto cleanup; } retval = 0; #ifdef DEBUG_ASN1 print_buffer_bin(*out, *out_len, "/tmp/kdc_enveloped_data"); #endif cleanup: if (p7 != NULL) PKCS7_free(p7); if (in != NULL) BIO_free(in); if (signed_data != NULL) free(signed_data); if (enc_data != NULL) free(enc_data); if (encerts != NULL) sk_X509_free(encerts); return retval; } krb5_error_code cms_envelopeddata_verify(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_preauthtype pa_type, int require_crl_checking, unsigned char *enveloped_data, unsigned int enveloped_data_len, unsigned char **data, unsigned int *data_len) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; PKCS7 *p7 = NULL; BIO *out = NULL; int i = 0; unsigned int size = 0; const unsigned char *p = enveloped_data; unsigned int tmp_buf_len = 0, tmp_buf2_len = 0, vfy_buf_len = 0; unsigned char *tmp_buf = NULL, *tmp_buf2 = NULL, *vfy_buf = NULL; int msg_type = 0; #ifdef DEBUG_ASN1 print_buffer_bin(enveloped_data, enveloped_data_len, "/tmp/client_envelopeddata"); #endif /* decode received PKCS7 message */ if ((p7 = d2i_PKCS7(NULL, &p, (int)enveloped_data_len)) == NULL) { unsigned long err = ERR_peek_error(); pkiDebug("failed to decode pkcs7\n"); krb5_set_error_message(context, retval, "%s\n", ERR_error_string(err, NULL)); goto cleanup; } /* verify that the received message is PKCS7 EnvelopedData message */ if (OBJ_obj2nid(p7->type) != NID_pkcs7_enveloped) { pkiDebug("Expected id-enveloped PKCS7 msg (received type = %d)\n", OBJ_obj2nid(p7->type)); krb5_set_error_message(context, retval, "wrong oid\n"); goto cleanup; } /* decrypt received PKCS7 message */ out = BIO_new(BIO_s_mem()); if (pkcs7_decrypt(context, id_cryptoctx, p7, out)) { pkiDebug("PKCS7 decryption successful\n"); } else { unsigned long err = ERR_peek_error(); if (err != 0) krb5_set_error_message(context, retval, "%s\n", ERR_error_string(err, NULL)); pkiDebug("PKCS7 decryption failed\n"); goto cleanup; } /* transfer the decoded PKCS7 SignedData message into a separate buffer */ for (;;) { if ((tmp_buf = realloc(tmp_buf, size + 1024 * 10)) == NULL) goto cleanup; i = BIO_read(out, &(tmp_buf[size]), 1024 * 10); if (i <= 0) break; else size += i; } tmp_buf_len = size; #ifdef DEBUG_ASN1 print_buffer_bin(tmp_buf, tmp_buf_len, "/tmp/client_enc_keypack"); #endif /* verify PKCS7 SignedData message */ switch (pa_type) { case KRB5_PADATA_PK_AS_REP: msg_type = CMS_ENVEL_SERVER; break; case KRB5_PADATA_PK_AS_REP_OLD: msg_type = CMS_SIGN_DRAFT9; break; default: pkiDebug("%s: unrecognized pa_type = %d\n", __FUNCTION__, pa_type); retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } /* * If this is the RFC style, wrap the signed data to make * decoding easier in the verify routine. * For draft9-compatible, we don't do anything because it * is already wrapped. */ #ifdef LONGHORN_BETA_COMPAT /* * The Longhorn server returns the expected RFC-style data, but * it is missing the sequence tag and length, so it requires * special processing when wrapping. * This will hopefully be fixed before the final release and * this can all be removed. */ if (msg_type == CMS_ENVEL_SERVER || longhorn == 1) { retval = wrap_signeddata(tmp_buf, tmp_buf_len, &tmp_buf2, &tmp_buf2_len, longhorn); if (retval) { pkiDebug("failed to encode signeddata\n"); goto cleanup; } vfy_buf = tmp_buf2; vfy_buf_len = tmp_buf2_len; } else { vfy_buf = tmp_buf; vfy_buf_len = tmp_buf_len; } #else if (msg_type == CMS_ENVEL_SERVER) { retval = wrap_signeddata(tmp_buf, tmp_buf_len, &tmp_buf2, &tmp_buf2_len); if (retval) { pkiDebug("failed to encode signeddata\n"); goto cleanup; } vfy_buf = tmp_buf2; vfy_buf_len = tmp_buf2_len; } else { vfy_buf = tmp_buf; vfy_buf_len = tmp_buf_len; } #endif #ifdef DEBUG_ASN1 print_buffer_bin(vfy_buf, vfy_buf_len, "/tmp/client_enc_keypack2"); #endif retval = cms_signeddata_verify(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, msg_type, require_crl_checking, vfy_buf, vfy_buf_len, data, data_len, NULL, NULL); if (!retval) pkiDebug("PKCS7 Verification Success\n"); else { pkiDebug("PKCS7 Verification Failure\n"); goto cleanup; } retval = 0; cleanup: if (p7 != NULL) PKCS7_free(p7); if (out != NULL) BIO_free(out); if (tmp_buf != NULL) free(tmp_buf); if (tmp_buf2 != NULL) free(tmp_buf2); return retval; } /* ARGSUSED */ static krb5_error_code crypto_retrieve_X509_sans(krb5_context context, pkinit_plg_crypto_context plgctx, pkinit_req_crypto_context reqctx, X509 *cert, krb5_principal **princs_ret, krb5_principal **upn_ret, unsigned char ***dns_ret) { krb5_error_code retval = EINVAL; char buf[DN_BUF_LEN]; int p = 0, u = 0, d = 0; krb5_principal *princs = NULL; krb5_principal *upns = NULL; unsigned char **dnss = NULL; int i, num_found = 0; if (princs_ret == NULL && upn_ret == NULL && dns_ret == NULL) { pkiDebug("%s: nowhere to return any values!\n", __FUNCTION__); return retval; } if (cert == NULL) { pkiDebug("%s: no certificate!\n", __FUNCTION__); return retval; } X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); pkiDebug("%s: looking for SANs in cert = %s\n", __FUNCTION__, buf); if ((i = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1)) >= 0) { X509_EXTENSION *ext = NULL; GENERAL_NAMES *ialt = NULL; GENERAL_NAME *gen = NULL; int ret = 0; unsigned int num_sans = 0; if (!(ext = X509_get_ext(cert, i)) || !(ialt = X509V3_EXT_d2i(ext))) { pkiDebug("%s: found no subject alt name extensions\n", __FUNCTION__); goto cleanup; } num_sans = sk_GENERAL_NAME_num(ialt); pkiDebug("%s: found %d subject alt name extension(s)\n", __FUNCTION__, num_sans); /* OK, we're likely returning something. Allocate return values */ if (princs_ret != NULL) { princs = calloc(num_sans + 1, sizeof(krb5_principal)); if (princs == NULL) { retval = ENOMEM; goto cleanup; } } if (upn_ret != NULL) { upns = calloc(num_sans + 1, sizeof(krb5_principal)); if (upns == NULL) { retval = ENOMEM; goto cleanup; } } if (dns_ret != NULL) { dnss = calloc(num_sans + 1, sizeof(*dnss)); if (dnss == NULL) { retval = ENOMEM; goto cleanup; } } for (i = 0; i < num_sans; i++) { krb5_data name = { 0, 0, NULL }; gen = sk_GENERAL_NAME_value(ialt, i); switch (gen->type) { case GEN_OTHERNAME: name.length = gen->d.otherName->value->value.sequence->length; name.data = (char *)gen->d.otherName->value->value.sequence->data; if (princs != NULL && OBJ_cmp(plgctx->id_pkinit_san, gen->d.otherName->type_id) == 0) { #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)name.data, name.length, "/tmp/pkinit_san"); #endif ret = k5int_decode_krb5_principal_name(&name, &princs[p]); if (ret) { pkiDebug("%s: failed decoding pkinit san value\n", __FUNCTION__); } else { p++; num_found++; } } else if (upns != NULL && OBJ_cmp(plgctx->id_ms_san_upn, gen->d.otherName->type_id) == 0) { ret = krb5_parse_name(context, name.data, &upns[u]); if (ret) { pkiDebug("%s: failed parsing ms-upn san value\n", __FUNCTION__); } else { u++; num_found++; } } else { pkiDebug("%s: unrecognized othername oid in SAN\n", __FUNCTION__); continue; } break; case GEN_DNS: if (dnss != NULL) { pkiDebug("%s: found dns name = %s\n", __FUNCTION__, gen->d.dNSName->data); dnss[d] = (unsigned char *) strdup((char *)gen->d.dNSName->data); if (dnss[d] == NULL) { pkiDebug("%s: failed to duplicate dns name\n", __FUNCTION__); } else { d++; num_found++; } } break; default: pkiDebug("%s: SAN type = %d expecting %d\n", __FUNCTION__, gen->type, GEN_OTHERNAME); } } sk_GENERAL_NAME_pop_free(ialt, GENERAL_NAME_free); } retval = 0; if (princs) *princs_ret = princs; if (upns) *upn_ret = upns; if (dnss) *dns_ret = dnss; cleanup: if (retval) { if (princs != NULL) { for (i = 0; princs[i] != NULL; i++) krb5_free_principal(context, princs[i]); free(princs); } if (upns != NULL) { for (i = 0; upns[i] != NULL; i++) krb5_free_principal(context, upns[i]); free(upns); } if (dnss != NULL) { for (i = 0; dnss[i] != NULL; i++) free(dnss[i]); free(dnss); } } return retval; } /* ARGSUSED */ krb5_error_code crypto_retrieve_cert_sans(krb5_context context, pkinit_plg_crypto_context plgctx, pkinit_req_crypto_context reqctx, pkinit_identity_crypto_context idctx, krb5_principal **princs_ret, krb5_principal **upn_ret, unsigned char ***dns_ret) { krb5_error_code retval = EINVAL; if (reqctx->received_cert == NULL) { pkiDebug("%s: No certificate!\n", __FUNCTION__); return retval; } return crypto_retrieve_X509_sans(context, plgctx, reqctx, reqctx->received_cert, princs_ret, upn_ret, dns_ret); } /* ARGSUSED */ krb5_error_code crypto_check_cert_eku(krb5_context context, pkinit_plg_crypto_context plgctx, pkinit_req_crypto_context reqctx, pkinit_identity_crypto_context idctx, int checking_kdc_cert, int allow_secondary_usage, int *valid_eku) { char buf[DN_BUF_LEN]; int found_eku = 0; krb5_error_code retval = EINVAL; int i; /* Solaris Kerberos */ if (valid_eku == NULL) return retval; *valid_eku = 0; if (reqctx->received_cert == NULL) goto cleanup; X509_NAME_oneline(X509_get_subject_name(reqctx->received_cert), buf, sizeof(buf)); pkiDebug("%s: looking for EKUs in cert = %s\n", __FUNCTION__, buf); if ((i = X509_get_ext_by_NID(reqctx->received_cert, NID_ext_key_usage, -1)) >= 0) { EXTENDED_KEY_USAGE *extusage; extusage = X509_get_ext_d2i(reqctx->received_cert, NID_ext_key_usage, NULL, NULL); if (extusage) { pkiDebug("%s: found eku info in the cert\n", __FUNCTION__); for (i = 0; found_eku == 0 && i < sk_ASN1_OBJECT_num(extusage); i++) { ASN1_OBJECT *tmp_oid; tmp_oid = sk_ASN1_OBJECT_value(extusage, i); pkiDebug("%s: checking eku %d of %d, allow_secondary = %d\n", __FUNCTION__, i+1, sk_ASN1_OBJECT_num(extusage), allow_secondary_usage); if (checking_kdc_cert) { if ((OBJ_cmp(tmp_oid, plgctx->id_pkinit_KPKdc) == 0) || (allow_secondary_usage && OBJ_cmp(tmp_oid, plgctx->id_kp_serverAuth) == 0)) found_eku = 1; } else { if ((OBJ_cmp(tmp_oid, plgctx->id_pkinit_KPClientAuth) == 0) || (allow_secondary_usage && OBJ_cmp(tmp_oid, plgctx->id_ms_kp_sc_logon) == 0)) found_eku = 1; } } } EXTENDED_KEY_USAGE_free(extusage); if (found_eku) { ASN1_BIT_STRING *usage = NULL; pkiDebug("%s: found acceptable EKU, checking for digitalSignature\n", __FUNCTION__); /* check that digitalSignature KeyUsage is present */ if ((usage = X509_get_ext_d2i(reqctx->received_cert, NID_key_usage, NULL, NULL))) { if (!ku_reject(reqctx->received_cert, X509v3_KU_DIGITAL_SIGNATURE)) { pkiDebug("%s: found digitalSignature KU\n", __FUNCTION__); *valid_eku = 1; } else pkiDebug("%s: didn't find digitalSignature KU\n", __FUNCTION__); } ASN1_BIT_STRING_free(usage); } } retval = 0; cleanup: pkiDebug("%s: returning retval %d, valid_eku %d\n", __FUNCTION__, retval, *valid_eku); return retval; } krb5_error_code pkinit_octetstring2key(krb5_context context, krb5_enctype etype, unsigned char *key, unsigned int dh_key_len, krb5_keyblock * key_block) { krb5_error_code retval; unsigned char *buf = NULL; unsigned char md[SHA_DIGEST_LENGTH]; unsigned char counter; size_t keybytes, keylength, offset; krb5_data random_data; if ((buf = (unsigned char *) malloc(dh_key_len)) == NULL) { retval = ENOMEM; goto cleanup; } (void) memset(buf, 0, dh_key_len); counter = 0; offset = 0; do { SHA_CTX c; SHA1_Init(&c); SHA1_Update(&c, &counter, 1); SHA1_Update(&c, key, dh_key_len); SHA1_Final(md, &c); if (dh_key_len - offset < sizeof(md)) (void) memcpy(buf + offset, md, dh_key_len - offset); else (void) memcpy(buf + offset, md, sizeof(md)); offset += sizeof(md); counter++; } while (offset < dh_key_len); /* Solaris Kerberos */ key_block->magic = KV5M_KEYBLOCK; key_block->enctype = etype; retval = krb5_c_keylengths(context, etype, &keybytes, &keylength); if (retval) goto cleanup; key_block->length = keylength; key_block->contents = calloc(keylength, sizeof(unsigned char *)); if (key_block->contents == NULL) { retval = ENOMEM; goto cleanup; } random_data.length = keybytes; random_data.data = (char *)buf; retval = krb5_c_random_to_key(context, etype, &random_data, key_block); cleanup: if (buf != NULL) free(buf); if (retval && key_block->contents != NULL && key_block->length != 0) { (void) memset(key_block->contents, 0, key_block->length); key_block->length = 0; } return retval; } /* ARGSUSED */ krb5_error_code client_create_dh(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context cryptoctx, pkinit_identity_crypto_context id_cryptoctx, int dh_size, unsigned char **dh_params, unsigned int *dh_params_len, unsigned char **dh_pubkey, unsigned int *dh_pubkey_len) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; unsigned char *buf = NULL; int dh_err = 0; ASN1_INTEGER *asn_pub_key = NULL; BIGNUM *p, *g, *q; const BIGNUM *pub_key; if (cryptoctx->dh == NULL) { if ((cryptoctx->dh = DH_new()) == NULL) goto cleanup; if ((g = BN_new()) == NULL || (q = BN_new()) == NULL) goto cleanup; switch(dh_size) { case 1024: pkiDebug("client uses 1024 DH keys\n"); cryptoctx->dh = make_dhprime(pkinit_1024_dhprime, sizeof(pkinit_1024_dhprime)); break; case 2048: pkiDebug("client uses 2048 DH keys\n"); cryptoctx->dh = make_dhprime(pkinit_2048_dhprime, sizeof(pkinit_2048_dhprime)); break; case 4096: pkiDebug("client uses 4096 DH keys\n"); cryptoctx->dh = make_dhprime(pkinit_4096_dhprime, sizeof(pkinit_4096_dhprime)); break; } if (cryptoctx->dh == NULL) goto cleanup; } DH_generate_key(cryptoctx->dh); DH_get0_key(cryptoctx->dh, &pub_key, NULL); /* Solaris Kerberos */ #ifdef DEBUG DH_check(cryptoctx->dh, &dh_err); if (dh_err != 0) { pkiDebug("Warning: dh_check failed with %d\n", dh_err); if (dh_err & DH_CHECK_P_NOT_PRIME) pkiDebug("p value is not prime\n"); if (dh_err & DH_CHECK_P_NOT_SAFE_PRIME) pkiDebug("p value is not a safe prime\n"); if (dh_err & DH_UNABLE_TO_CHECK_GENERATOR) pkiDebug("unable to check the generator value\n"); if (dh_err & DH_NOT_SUITABLE_GENERATOR) pkiDebug("the g value is not a generator\n"); } #endif #ifdef DEBUG_DH print_dh(cryptoctx->dh, "client's DH params\n"); print_pubkey(pub_key, "client's pub_key="); #endif DH_check_pub_key(cryptoctx->dh, pub_key, &dh_err); if (dh_err != 0) { pkiDebug("dh_check_pub_key failed with %d\n", dh_err); goto cleanup; } /* pack DHparams */ /* aglo: usually we could just call i2d_DHparams to encode DH params * however, PKINIT requires RFC3279 encoding and openssl does pkcs#3. */ DH_get0_pqg(cryptoctx->dh, (const BIGNUM **)&p, (const BIGNUM **)&q, (const BIGNUM **)&g); retval = pkinit_encode_dh_params(p, g, q, dh_params, dh_params_len); if (retval) goto cleanup; /* pack DH public key */ /* Diffie-Hellman public key must be ASN1 encoded as an INTEGER; this * encoding shall be used as the contents (the value) of the * subjectPublicKey component (a BIT STRING) of the SubjectPublicKeyInfo * data element */ if ((asn_pub_key = BN_to_ASN1_INTEGER(pub_key, NULL)) == NULL) goto cleanup; *dh_pubkey_len = i2d_ASN1_INTEGER(asn_pub_key, NULL); if ((buf = *dh_pubkey = (unsigned char *) malloc((size_t) *dh_pubkey_len)) == NULL) { retval = ENOMEM; goto cleanup; } i2d_ASN1_INTEGER(asn_pub_key, &buf); if (asn_pub_key != NULL) ASN1_INTEGER_free(asn_pub_key); retval = 0; return retval; cleanup: if (cryptoctx->dh != NULL) DH_free(cryptoctx->dh); cryptoctx->dh = NULL; if (*dh_params != NULL) free(*dh_params); *dh_params = NULL; if (*dh_pubkey != NULL) free(*dh_pubkey); *dh_pubkey = NULL; if (asn_pub_key != NULL) ASN1_INTEGER_free(asn_pub_key); return retval; } /* ARGSUSED */ krb5_error_code client_process_dh(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context cryptoctx, pkinit_identity_crypto_context id_cryptoctx, unsigned char *subjectPublicKey_data, unsigned int subjectPublicKey_length, unsigned char **client_key, unsigned int *client_key_len) { /* Solaris Kerberos */ krb5_error_code retval = KRB5_PREAUTH_FAILED; BIGNUM *server_pub_key = NULL; ASN1_INTEGER *pub_key = NULL; const unsigned char *p = NULL; unsigned char *data = NULL; long data_len; /* decode subjectPublicKey (retrieve INTEGER from OCTET_STRING) */ if (der_decode_data(subjectPublicKey_data, (long)subjectPublicKey_length, &data, &data_len) != 0) { pkiDebug("failed to decode subjectPublicKey\n"); /* Solaris Kerberos */ retval = KRB5_PREAUTH_FAILED; goto cleanup; } *client_key_len = DH_size(cryptoctx->dh); if ((*client_key = (unsigned char *) malloc((size_t) *client_key_len)) == NULL) { retval = ENOMEM; goto cleanup; } p = data; if ((pub_key = d2i_ASN1_INTEGER(NULL, &p, data_len)) == NULL) goto cleanup; if ((server_pub_key = ASN1_INTEGER_to_BN(pub_key, NULL)) == NULL) goto cleanup; DH_compute_key(*client_key, server_pub_key, cryptoctx->dh); #ifdef DEBUG_DH print_pubkey(server_pub_key, "server's pub_key="); pkiDebug("client secret key (%d)= ", *client_key_len); print_buffer(*client_key, *client_key_len); #endif retval = 0; if (server_pub_key != NULL) BN_free(server_pub_key); if (pub_key != NULL) ASN1_INTEGER_free(pub_key); if (data != NULL) free (data); return retval; cleanup: if (*client_key != NULL) free(*client_key); *client_key = NULL; if (pub_key != NULL) ASN1_INTEGER_free(pub_key); if (data != NULL) free (data); return retval; } /* ARGSUSED */ krb5_error_code server_check_dh(krb5_context context, pkinit_plg_crypto_context cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_octet_data *dh_params, int minbits) { DH *dh = NULL; unsigned char *tmp = NULL; int dh_prime_bits; krb5_error_code retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; const BIGNUM *p, *g, *q, *p2; tmp = dh_params->data; dh = DH_new(); dh = pkinit_decode_dh_params(&dh, &tmp, dh_params->length); if (dh == NULL) { pkiDebug("failed to decode dhparams\n"); goto cleanup; } DH_get0_pqg(dh, &p, &q, &g); /* KDC SHOULD check to see if the key parameters satisfy its policy */ dh_prime_bits = BN_num_bits(p); if (minbits && dh_prime_bits < minbits) { pkiDebug("client sent dh params with %d bits, we require %d\n", dh_prime_bits, minbits); goto cleanup; } /* check dhparams is group 2 */ DH_get0_pqg(cryptoctx->dh_1024, &p2, NULL, NULL); if (pkinit_check_dh_params(p2, p, g, q) == 0) { retval = 0; goto cleanup; } /* check dhparams is group 14 */ DH_get0_pqg(cryptoctx->dh_2048, &p2, NULL, NULL); if (pkinit_check_dh_params(p2, p, g, q) == 0) { retval = 0; goto cleanup; } /* check dhparams is group 16 */ DH_get0_pqg(cryptoctx->dh_4096, &p2, NULL, NULL); if (pkinit_check_dh_params(p2, p, g, q) == 0) { retval = 0; goto cleanup; } cleanup: if (retval == 0) req_cryptoctx->dh = dh; else DH_free(dh); return retval; } /* kdc's dh function */ /* ARGSUSED */ krb5_error_code server_process_dh(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context cryptoctx, pkinit_identity_crypto_context id_cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **dh_pubkey, unsigned int *dh_pubkey_len, unsigned char **server_key, unsigned int *server_key_len) { /* Solaris Kerberos */ krb5_error_code retval = KRB5KRB_ERR_GENERIC; DH *dh = NULL, *dh_server = NULL; const BIGNUM *p, *g, *q, *s_pub_key; BIGNUM *pub_key; unsigned char *s = NULL; ASN1_INTEGER *asn_pub_key = NULL; /* get client's received DH parameters that we saved in server_check_dh */ dh = cryptoctx->dh; dh_server = DH_new(); if (dh_server == NULL) goto cleanup; DH_get0_pqg(dh, &p, &g, &q); DH_set0_pqg(dh_server, BN_dup(p), BN_dup(g), BN_dup(q)); /* decode client's public key */ s = data; asn_pub_key = d2i_ASN1_INTEGER(NULL, (const unsigned char **)&s, (int)data_len); if (asn_pub_key == NULL) goto cleanup; pub_key = ASN1_INTEGER_to_BN(asn_pub_key, NULL); if (pub_key == NULL) goto cleanup; DH_set0_key(dh, pub_key, NULL); ASN1_INTEGER_free(asn_pub_key); if (!DH_generate_key(dh_server)) goto cleanup; /* generate DH session key */ *server_key_len = DH_size(dh_server); if ((*server_key = (unsigned char *) malloc((size_t)*server_key_len)) == NULL) goto cleanup; DH_compute_key(*server_key, pub_key, dh_server); DH_get0_key(dh_server, &s_pub_key, NULL); #ifdef DEBUG_DH print_dh(dh_server, "client&server's DH params\n"); print_pubkey(pub_key, "client's pub_key="); print_pubkey(s_pub_key, "server's pub_key="); pkiDebug("server secret key="); print_buffer(*server_key, *server_key_len); #endif /* KDC reply */ /* pack DH public key */ /* Diffie-Hellman public key must be ASN1 encoded as an INTEGER; this * encoding shall be used as the contents (the value) of the * subjectPublicKey component (a BIT STRING) of the SubjectPublicKeyInfo * data element */ if ((asn_pub_key = BN_to_ASN1_INTEGER(s_pub_key, NULL)) == NULL) goto cleanup; *dh_pubkey_len = i2d_ASN1_INTEGER(asn_pub_key, NULL); if ((s = *dh_pubkey = (unsigned char *) malloc((size_t)*dh_pubkey_len)) == NULL) goto cleanup; i2d_ASN1_INTEGER(asn_pub_key, &s); if (asn_pub_key != NULL) ASN1_INTEGER_free(asn_pub_key); retval = 0; if (dh_server != NULL) DH_free(dh_server); return retval; cleanup: if (dh_server != NULL) DH_free(dh_server); if (*dh_pubkey != NULL) free(*dh_pubkey); if (*server_key != NULL) free(*server_key); return retval; } /* * Solaris Kerberos: * Add locking around did_init to make it MT-safe. */ static krb5_error_code openssl_init() { krb5_error_code ret = 0; static int did_init = 0; static k5_mutex_t init_mutex = K5_MUTEX_PARTIAL_INITIALIZER; ret = k5_mutex_lock(&init_mutex); if (ret == 0) { if (!did_init) { /* initialize openssl routines */ #if OPENSSL_VERSION_NUMBER < 0x10100000L /* * As of version 1.1.0, OpenSSL will automatically allocate * resources as-needed. */ CRYPTO_malloc_init(); ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); #endif did_init++; } k5_mutex_unlock(&init_mutex); } return (ret); } static krb5_error_code pkinit_encode_dh_params(const BIGNUM *p, const BIGNUM *g, const BIGNUM *q, unsigned char **buf, unsigned int *buf_len) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; int bufsize = 0, r = 0; unsigned char *tmp = NULL; ASN1_INTEGER *ap = NULL, *ag = NULL, *aq = NULL; if ((ap = BN_to_ASN1_INTEGER(p, NULL)) == NULL) goto cleanup; if ((ag = BN_to_ASN1_INTEGER(g, NULL)) == NULL) goto cleanup; if ((aq = BN_to_ASN1_INTEGER(q, NULL)) == NULL) goto cleanup; bufsize = i2d_ASN1_INTEGER(ap, NULL); bufsize += i2d_ASN1_INTEGER(ag, NULL); bufsize += i2d_ASN1_INTEGER(aq, NULL); r = ASN1_object_size(1, bufsize, V_ASN1_SEQUENCE); tmp = *buf = (unsigned char *)malloc((size_t) r); if (tmp == NULL) goto cleanup; ASN1_put_object(&tmp, 1, bufsize, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL); i2d_ASN1_INTEGER(ap, &tmp); i2d_ASN1_INTEGER(ag, &tmp); i2d_ASN1_INTEGER(aq, &tmp); *buf_len = r; retval = 0; cleanup: if (ap != NULL) ASN1_INTEGER_free(ap); if (ag != NULL) ASN1_INTEGER_free(ag); if (aq != NULL) ASN1_INTEGER_free(aq); return retval; } #if OPENSSL_VERSION_NUMBER < 0x10100000L static DH * pkinit_decode_dh_params(DH ** a, unsigned char **pp, unsigned int len) { ASN1_INTEGER ai, *aip = NULL; long length = (long) len; M_ASN1_D2I_vars(a, DH *, DH_new); M_ASN1_D2I_Init(); M_ASN1_D2I_start_sequence(); aip = &ai; ai.data = NULL; ai.length = 0; M_ASN1_D2I_get_x(ASN1_INTEGER, aip, d2i_ASN1_INTEGER); if (aip == NULL) return NULL; else { (*a)->p = ASN1_INTEGER_to_BN(aip, NULL); if ((*a)->p == NULL) return NULL; if (ai.data != NULL) { OPENSSL_free(ai.data); ai.data = NULL; ai.length = 0; } } M_ASN1_D2I_get_x(ASN1_INTEGER, aip, d2i_ASN1_INTEGER); if (aip == NULL) return NULL; else { (*a)->g = ASN1_INTEGER_to_BN(aip, NULL); if ((*a)->g == NULL) return NULL; if (ai.data != NULL) { OPENSSL_free(ai.data); ai.data = NULL; ai.length = 0; } } M_ASN1_D2I_get_x(ASN1_INTEGER, aip, d2i_ASN1_INTEGER); if (aip == NULL) return NULL; else { (*a)->q = ASN1_INTEGER_to_BN(aip, NULL); if ((*a)->q == NULL) return NULL; if (ai.data != NULL) { OPENSSL_free(ai.data); ai.data = NULL; ai.length = 0; } } M_ASN1_D2I_end_sequence(); M_ASN1_D2I_Finish(a, DH_free, 0); } #else /* * This is taken from the internal dh_asn1.c file in OpenSSL 1.1, modified to * make q an optional field. */ typedef struct { ASN1_BIT_STRING *seed; BIGNUM *counter; } int_dhvparams; typedef struct { BIGNUM *p; BIGNUM *q; BIGNUM *g; BIGNUM *j; int_dhvparams *vparams; } int_dhx942_dh; ASN1_SEQUENCE(DHvparams) = { ASN1_SIMPLE(int_dhvparams, seed, ASN1_BIT_STRING), ASN1_SIMPLE(int_dhvparams, counter, BIGNUM) } static_ASN1_SEQUENCE_END_name(int_dhvparams, DHvparams) ASN1_SEQUENCE(DHxparams) = { ASN1_SIMPLE(int_dhx942_dh, p, BIGNUM), ASN1_SIMPLE(int_dhx942_dh, g, BIGNUM), ASN1_OPT(int_dhx942_dh, q, BIGNUM), ASN1_OPT(int_dhx942_dh, j, BIGNUM), ASN1_OPT(int_dhx942_dh, vparams, DHvparams), } static_ASN1_SEQUENCE_END_name(int_dhx942_dh, DHxparams) static DH * pkinit_decode_dh_params(DH **a, unsigned char **pp, unsigned int len) { int_dhx942_dh *params; DH *dh = *a; if (dh == NULL) return NULL; params = (int_dhx942_dh *)ASN1_item_d2i(NULL, (const unsigned char **)pp, len, ASN1_ITEM_rptr(DHxparams)); if (params == NULL) { DH_free(dh); return NULL; } DH_set0_pqg(dh, params->p, params->q, params->g); params->p = params->q = params->g = NULL; ASN1_item_free((ASN1_VALUE *)params, ASN1_ITEM_rptr(DHxparams)); return dh; } #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L || LIBRESSL_VERSION_NUMBER */ static krb5_error_code pkinit_create_sequence_of_principal_identifiers( krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, int type, krb5_data **out_data) { krb5_error_code retval = KRB5KRB_ERR_GENERIC; krb5_external_principal_identifier **krb5_trusted_certifiers = NULL; krb5_data *td_certifiers = NULL, *data = NULL; krb5_typed_data **typed_data = NULL; switch(type) { case TD_TRUSTED_CERTIFIERS: retval = create_krb5_trustedCertifiers(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, &krb5_trusted_certifiers); if (retval) { pkiDebug("create_krb5_trustedCertifiers failed\n"); goto cleanup; } break; case TD_INVALID_CERTIFICATES: retval = create_krb5_invalidCertificates(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, &krb5_trusted_certifiers); if (retval) { pkiDebug("create_krb5_invalidCertificates failed\n"); goto cleanup; } break; default: retval = -1; goto cleanup; } retval = k5int_encode_krb5_td_trusted_certifiers((const krb5_external_principal_identifier **)krb5_trusted_certifiers, &td_certifiers); if (retval) { pkiDebug("encode_krb5_td_trusted_certifiers failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)td_certifiers->data, td_certifiers->length, "/tmp/kdc_td_certifiers"); #endif typed_data = malloc (2 * sizeof(krb5_typed_data *)); if (typed_data == NULL) { retval = ENOMEM; goto cleanup; } typed_data[1] = NULL; init_krb5_typed_data(&typed_data[0]); if (typed_data[0] == NULL) { retval = ENOMEM; goto cleanup; } typed_data[0]->type = type; typed_data[0]->length = td_certifiers->length; typed_data[0]->data = (unsigned char *)td_certifiers->data; retval = k5int_encode_krb5_typed_data((const krb5_typed_data **)typed_data, &data); if (retval) { pkiDebug("encode_krb5_typed_data failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)data->data, data->length, "/tmp/kdc_edata"); #endif *out_data = (krb5_data *)malloc(sizeof(krb5_data)); (*out_data)->length = data->length; (*out_data)->data = (char *)malloc(data->length); (void) memcpy((*out_data)->data, data->data, data->length); retval = 0; cleanup: if (krb5_trusted_certifiers != NULL) free_krb5_external_principal_identifier(&krb5_trusted_certifiers); if (data != NULL) { if (data->data != NULL) free(data->data); free(data); } if (td_certifiers != NULL) free(td_certifiers); if (typed_data != NULL) free_krb5_typed_data(&typed_data); return retval; } krb5_error_code pkinit_create_td_trusted_certifiers(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_data **out_data) { krb5_error_code retval = KRB5KRB_ERR_GENERIC; retval = pkinit_create_sequence_of_principal_identifiers(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, TD_TRUSTED_CERTIFIERS, out_data); return retval; } krb5_error_code pkinit_create_td_invalid_certificate( krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_data **out_data) { krb5_error_code retval = KRB5KRB_ERR_GENERIC; retval = pkinit_create_sequence_of_principal_identifiers(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, TD_INVALID_CERTIFICATES, out_data); return retval; } /* ARGSUSED */ krb5_error_code pkinit_create_td_dh_parameters(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, pkinit_plg_opts *opts, krb5_data **out_data) { /* Solaris Kerberos */ krb5_error_code retval = KRB5KRB_ERR_GENERIC; unsigned int buf1_len = 0, buf2_len = 0, buf3_len = 0, i = 0; unsigned char *buf1 = NULL, *buf2 = NULL, *buf3 = NULL; krb5_typed_data **typed_data = NULL; krb5_data *data = NULL, *encoded_algId = NULL; krb5_algorithm_identifier **algId = NULL; const BIGNUM *p, *q, *g; /* Solaris Kerberos */ if (opts->dh_min_bits > 4096) { retval = EINVAL; goto cleanup; } if (opts->dh_min_bits <= 1024) { DH_get0_pqg(plg_cryptoctx->dh_1024, &p, &q, &g); retval = pkinit_encode_dh_params(p, g, q, &buf1, &buf1_len); if (retval) goto cleanup; } if (opts->dh_min_bits <= 2048) { DH_get0_pqg(plg_cryptoctx->dh_2048, &p, &q, &g); retval = pkinit_encode_dh_params(p, g, q, &buf2, &buf2_len); if (retval) goto cleanup; } DH_get0_pqg(plg_cryptoctx->dh_4096, &p, &q, &g); retval = pkinit_encode_dh_params(p, g, q, &buf3, &buf3_len); if (retval) goto cleanup; if (opts->dh_min_bits <= 1024) { algId = malloc(4 * sizeof(krb5_algorithm_identifier *)); if (algId == NULL) goto cleanup; algId[3] = NULL; algId[0] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier)); if (algId[0] == NULL) goto cleanup; algId[0]->parameters.data = (unsigned char *)malloc(buf2_len); if (algId[0]->parameters.data == NULL) goto cleanup; (void) memcpy(algId[0]->parameters.data, buf2, buf2_len); algId[0]->parameters.length = buf2_len; algId[0]->algorithm = dh_oid; algId[1] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier)); if (algId[1] == NULL) goto cleanup; algId[1]->parameters.data = (unsigned char *)malloc(buf3_len); if (algId[1]->parameters.data == NULL) goto cleanup; (void) memcpy(algId[1]->parameters.data, buf3, buf3_len); algId[1]->parameters.length = buf3_len; algId[1]->algorithm = dh_oid; algId[2] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier)); if (algId[2] == NULL) goto cleanup; algId[2]->parameters.data = (unsigned char *)malloc(buf1_len); if (algId[2]->parameters.data == NULL) goto cleanup; (void) memcpy(algId[2]->parameters.data, buf1, buf1_len); algId[2]->parameters.length = buf1_len; algId[2]->algorithm = dh_oid; } else if (opts->dh_min_bits <= 2048) { algId = malloc(3 * sizeof(krb5_algorithm_identifier *)); if (algId == NULL) goto cleanup; algId[2] = NULL; algId[0] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier)); if (algId[0] == NULL) goto cleanup; algId[0]->parameters.data = (unsigned char *)malloc(buf2_len); if (algId[0]->parameters.data == NULL) goto cleanup; (void) memcpy(algId[0]->parameters.data, buf2, buf2_len); algId[0]->parameters.length = buf2_len; algId[0]->algorithm = dh_oid; algId[1] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier)); if (algId[1] == NULL) goto cleanup; algId[1]->parameters.data = (unsigned char *)malloc(buf3_len); if (algId[1]->parameters.data == NULL) goto cleanup; (void) memcpy(algId[1]->parameters.data, buf3, buf3_len); algId[1]->parameters.length = buf3_len; algId[1]->algorithm = dh_oid; } else if (opts->dh_min_bits <= 4096) { algId = malloc(2 * sizeof(krb5_algorithm_identifier *)); if (algId == NULL) goto cleanup; algId[1] = NULL; algId[0] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier)); if (algId[0] == NULL) goto cleanup; algId[0]->parameters.data = (unsigned char *)malloc(buf3_len); if (algId[0]->parameters.data == NULL) goto cleanup; (void) memcpy(algId[0]->parameters.data, buf3, buf3_len); algId[0]->parameters.length = buf3_len; algId[0]->algorithm = dh_oid; } retval = k5int_encode_krb5_td_dh_parameters((const krb5_algorithm_identifier **)algId, &encoded_algId); if (retval) goto cleanup; #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)encoded_algId->data, encoded_algId->length, "/tmp/kdc_td_dh_params"); #endif typed_data = malloc (2 * sizeof(krb5_typed_data *)); if (typed_data == NULL) { retval = ENOMEM; goto cleanup; } typed_data[1] = NULL; init_krb5_typed_data(&typed_data[0]); if (typed_data == NULL) { retval = ENOMEM; goto cleanup; } typed_data[0]->type = TD_DH_PARAMETERS; typed_data[0]->length = encoded_algId->length; typed_data[0]->data = (unsigned char *)encoded_algId->data; retval = k5int_encode_krb5_typed_data((const krb5_typed_data**)typed_data, &data); if (retval) { pkiDebug("encode_krb5_typed_data failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)data->data, data->length, "/tmp/kdc_edata"); #endif *out_data = (krb5_data *)malloc(sizeof(krb5_data)); if (*out_data == NULL) goto cleanup; (*out_data)->length = data->length; (*out_data)->data = (char *)malloc(data->length); if ((*out_data)->data == NULL) { free(*out_data); *out_data = NULL; goto cleanup; } (void) memcpy((*out_data)->data, data->data, data->length); retval = 0; cleanup: if (buf1 != NULL) free(buf1); if (buf2 != NULL) free(buf2); if (buf3 != NULL) free(buf3); if (data != NULL) { if (data->data != NULL) free(data->data); free(data); } if (typed_data != NULL) free_krb5_typed_data(&typed_data); if (encoded_algId != NULL) free(encoded_algId); if (algId != NULL) { while(algId[i] != NULL) { if (algId[i]->parameters.data != NULL) free(algId[i]->parameters.data); free(algId[i]); i++; } free(algId); } return retval; } /* ARGSUSED */ krb5_error_code pkinit_check_kdc_pkid(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, unsigned char *pdid_buf, unsigned int pkid_len, int *valid_kdcPkId) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; PKCS7_ISSUER_AND_SERIAL *is = NULL; const unsigned char *p = pdid_buf; int status = 1; X509 *kdc_cert = sk_X509_value(id_cryptoctx->my_certs, id_cryptoctx->cert_index); *valid_kdcPkId = 0; pkiDebug("found kdcPkId in AS REQ\n"); is = d2i_PKCS7_ISSUER_AND_SERIAL(NULL, &p, (int)pkid_len); if (is == NULL) goto cleanup; status = X509_NAME_cmp(X509_get_issuer_name(kdc_cert), is->issuer); if (!status) { status = ASN1_INTEGER_cmp(X509_get_serialNumber(kdc_cert), is->serial); if (!status) *valid_kdcPkId = 1; } retval = 0; cleanup: X509_NAME_free(is->issuer); ASN1_INTEGER_free(is->serial); free(is); return retval; } static int pkinit_check_dh_params(const BIGNUM *p1, const BIGNUM *p2, const BIGNUM *g1, const BIGNUM *q1) { BIGNUM *g2 = NULL, *q2 = NULL; /* Solaris Kerberos */ int retval = EINVAL; if (!BN_cmp(p1, p2)) { g2 = BN_new(); BN_set_word(g2, DH_GENERATOR_2); if (!BN_cmp(g1, g2)) { q2 = BN_new(); BN_rshift1(q2, p1); if (!BN_cmp(q1, q2)) { pkiDebug("good %d dhparams\n", BN_num_bits(p1)); retval = 0; } else pkiDebug("bad group 2 q dhparameter\n"); BN_free(q2); } else pkiDebug("bad g dhparameter\n"); BN_free(g2); } else pkiDebug("p is not well-known group 2 dhparameter\n"); return retval; } /* ARGSUSED */ krb5_error_code pkinit_process_td_dh_params(krb5_context context, pkinit_plg_crypto_context cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_algorithm_identifier **algId, int *new_dh_size) { krb5_error_code retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; int i = 0, use_sent_dh = 0, ok = 0; pkiDebug("dh parameters\n"); while (algId[i] != NULL) { DH *dh = NULL; unsigned char *tmp = NULL; const BIGNUM *p, *g, *q, *p2; int dh_prime_bits = 0; if (algId[i]->algorithm.length != dh_oid.length || memcmp(algId[i]->algorithm.data, dh_oid.data, dh_oid.length)) goto cleanup; tmp = algId[i]->parameters.data; dh = DH_new(); dh = pkinit_decode_dh_params(&dh, &tmp, algId[i]->parameters.length); dh_prime_bits = DH_bits(dh); pkiDebug("client sent %d DH bits server prefers %d DH bits\n", *new_dh_size, dh_prime_bits); DH_get0_pqg(dh, &p, &q, &g); switch(dh_prime_bits) { case 1024: DH_get0_pqg(cryptoctx->dh_1024, &p2, NULL, NULL); if (pkinit_check_dh_params(p2, p, g, q) == 0) { *new_dh_size = 1024; ok = 1; } break; case 2048: DH_get0_pqg(cryptoctx->dh_2048, &p2, NULL, NULL); if (pkinit_check_dh_params(p2, p, g, q) == 0) { *new_dh_size = 2048; ok = 1; } break; case 4096: DH_get0_pqg(cryptoctx->dh_4096, &p2, NULL, NULL); if (pkinit_check_dh_params(p2, p, g, q) == 0) { *new_dh_size = 4096; ok = 1; } break; default: break; } if (!ok) { DH_check(dh, &retval); if (retval != 0) { pkiDebug("DH parameters provided by server are unacceptable\n"); retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; } else { use_sent_dh = 1; ok = 1; } } if (!use_sent_dh) DH_free(dh); if (ok) { if (req_cryptoctx->dh != NULL) { DH_free(req_cryptoctx->dh); req_cryptoctx->dh = NULL; } if (use_sent_dh) req_cryptoctx->dh = dh; break; } i++; } if (ok) retval = 0; cleanup: return retval; } /* ARGSUSED */ static int openssl_callback(int ok, X509_STORE_CTX * ctx) { #ifdef DEBUG if (!ok) { char buf[DN_BUF_LEN]; X509_NAME_oneline(X509_get_subject_name(ctx->current_cert), buf, sizeof(buf)); pkiDebug("cert = %s\n", buf); pkiDebug("callback function: %d (%s)\n", ctx->error, X509_verify_cert_error_string(ctx->error)); } #endif return ok; } static int openssl_callback_ignore_crls(int ok, X509_STORE_CTX * ctx) { if (!ok) return (X509_STORE_CTX_get_error(ctx) == X509_V_ERR_UNABLE_TO_GET_CRL); return ok; } static ASN1_OBJECT * pkinit_pkcs7type2oid(pkinit_plg_crypto_context cryptoctx, int pkcs7_type) { switch (pkcs7_type) { case CMS_SIGN_CLIENT: return cryptoctx->id_pkinit_authData; case CMS_SIGN_DRAFT9: return OBJ_nid2obj(NID_pkcs7_data); case CMS_SIGN_SERVER: return cryptoctx->id_pkinit_DHKeyData; case CMS_ENVEL_SERVER: return cryptoctx->id_pkinit_rkeyData; default: return NULL; } } #ifdef LONGHORN_BETA_COMPAT #if 0 /* * This is a version that worked with Longhorn Beta 3. */ static int wrap_signeddata(unsigned char *data, unsigned int data_len, unsigned char **out, unsigned int *out_len, int is_longhorn_server) { unsigned int orig_len = 0, oid_len = 0, tot_len = 0; ASN1_OBJECT *oid = NULL; unsigned char *p = NULL; pkiDebug("%s: This is the Longhorn version and is_longhorn_server = %d\n", __FUNCTION__, is_longhorn_server); /* Get length to wrap the original data with SEQUENCE tag */ tot_len = orig_len = ASN1_object_size(1, (int)data_len, V_ASN1_SEQUENCE); if (is_longhorn_server == 0) { /* Add the signedData OID and adjust lengths */ oid = OBJ_nid2obj(NID_pkcs7_signed); oid_len = i2d_ASN1_OBJECT(oid, NULL); tot_len = ASN1_object_size(1, (int)(orig_len+oid_len), V_ASN1_SEQUENCE); } p = *out = (unsigned char *)malloc(tot_len); if (p == NULL) return -1; if (is_longhorn_server == 0) { ASN1_put_object(&p, 1, (int)(orig_len+oid_len), V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL); i2d_ASN1_OBJECT(oid, &p); ASN1_put_object(&p, 1, (int)data_len, 0, V_ASN1_CONTEXT_SPECIFIC); } else { ASN1_put_object(&p, 1, (int)data_len, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL); } memcpy(p, data, data_len); *out_len = tot_len; return 0; } #else /* * This is a version that works with a patched Longhorn KDC. * (Which should match SP1 ??). */ static int wrap_signeddata(unsigned char *data, unsigned int data_len, unsigned char **out, unsigned int *out_len, int is_longhorn_server) { unsigned int oid_len = 0, tot_len = 0, wrap_len = 0, tag_len = 0; ASN1_OBJECT *oid = NULL; unsigned char *p = NULL; pkiDebug("%s: This is the Longhorn version and is_longhorn_server = %d\n", __FUNCTION__, is_longhorn_server); /* New longhorn is missing another sequence */ if (is_longhorn_server == 1) wrap_len = ASN1_object_size(1, (int)(data_len), V_ASN1_SEQUENCE); else wrap_len = data_len; /* Get length to wrap the original data with SEQUENCE tag */ tag_len = ASN1_object_size(1, (int)wrap_len, V_ASN1_SEQUENCE); /* Always add oid */ oid = OBJ_nid2obj(NID_pkcs7_signed); oid_len = i2d_ASN1_OBJECT(oid, NULL); oid_len += tag_len; tot_len = ASN1_object_size(1, (int)(oid_len), V_ASN1_SEQUENCE); p = *out = (unsigned char *)malloc(tot_len); if (p == NULL) return -1; ASN1_put_object(&p, 1, (int)(oid_len), V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL); i2d_ASN1_OBJECT(oid, &p); ASN1_put_object(&p, 1, (int)wrap_len, 0, V_ASN1_CONTEXT_SPECIFIC); /* Wrap in extra seq tag */ if (is_longhorn_server == 1) { ASN1_put_object(&p, 1, (int)data_len, V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL); } (void) memcpy(p, data, data_len); *out_len = tot_len; return 0; } #endif #else static int wrap_signeddata(unsigned char *data, unsigned int data_len, unsigned char **out, unsigned int *out_len) { unsigned int orig_len = 0, oid_len = 0, tot_len = 0; ASN1_OBJECT *oid = NULL; unsigned char *p = NULL; /* Get length to wrap the original data with SEQUENCE tag */ tot_len = orig_len = ASN1_object_size(1, (int)data_len, V_ASN1_SEQUENCE); /* Add the signedData OID and adjust lengths */ oid = OBJ_nid2obj(NID_pkcs7_signed); oid_len = i2d_ASN1_OBJECT(oid, NULL); tot_len = ASN1_object_size(1, (int)(orig_len+oid_len), V_ASN1_SEQUENCE); p = *out = (unsigned char *)malloc(tot_len); if (p == NULL) return -1; ASN1_put_object(&p, 1, (int)(orig_len+oid_len), V_ASN1_SEQUENCE, V_ASN1_UNIVERSAL); i2d_ASN1_OBJECT(oid, &p); ASN1_put_object(&p, 1, (int)data_len, 0, V_ASN1_CONTEXT_SPECIFIC); (void) memcpy(p, data, data_len); *out_len = tot_len; return 0; } #endif static int prepare_enc_data(unsigned char *indata, int indata_len, unsigned char **outdata, int *outdata_len) { int tag, class; long tlen, slen; const uint8_t *p = indata, *oldp; /* Top-bit set means that the conversion failed. */ if (ASN1_get_object(&p, &slen, &tag, &class, indata_len) & 0x80) return EINVAL; if (tag != V_ASN1_SEQUENCE) return EINVAL; oldp = p; if (ASN1_get_object(&p, &tlen, &tag, &class, slen) & 0x80) return EINVAL; p += tlen; slen -= (p - oldp); if (ASN1_get_object(&p, &tlen, &tag, &class, slen) & 0x80) return EINVAL; *outdata = malloc(tlen); if (*outdata == NULL) return ENOMEM; memcpy(*outdata, p, tlen); *outdata_len = tlen; return 0; } #ifndef WITHOUT_PKCS11 static void * pkinit_C_LoadModule(const char *modname, CK_FUNCTION_LIST_PTR_PTR p11p) { void *handle; CK_RV (*getflist)(CK_FUNCTION_LIST_PTR_PTR); pkiDebug("loading module \"%s\"... ", modname); /* Solaris Kerberos */ handle = dlopen(modname, RTLD_NOW | RTLD_GROUP); if (handle == NULL) { pkiDebug("not found\n"); return NULL; } getflist = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR)) dlsym(handle, "C_GetFunctionList"); if (getflist == NULL || (*getflist)(p11p) != CKR_OK) { (void) dlclose(handle); pkiDebug("failed\n"); return NULL; } pkiDebug("ok\n"); return handle; } static CK_RV pkinit_C_UnloadModule(void *handle) { /* Solaris Kerberos */ if (dlclose(handle) != 0) return CKR_GENERAL_ERROR; return CKR_OK; } /* * Solaris Kerberos: this is a new function that does not exist yet in the MIT * code. * * labelstr will be C string containing token label with trailing white space * removed. */ static void trim_token_label(CK_TOKEN_INFO *tinfo, char *labelstr, unsigned int labelstr_len) { int i; assert(labelstr_len > sizeof (tinfo->label)); /* * \0 terminate labelstr in case the last char in the token label is * non-whitespace */ labelstr[sizeof (tinfo->label)] = '\0'; (void) memcpy(labelstr, (char *) tinfo->label, sizeof (tinfo->label)); /* init i so terminating \0 is skipped */ for (i = sizeof (tinfo->label) - 1; i >= 0; i--) { if (labelstr[i] == ' ') labelstr[i] = '\0'; else break; } } /* * Solaris Kerberos: this is a new function that does not exist yet in the MIT * code. */ static krb5_error_code pkinit_prompt_user(krb5_context context, pkinit_identity_crypto_context cctx, krb5_data *reply, char *prompt, int hidden) { krb5_error_code r; krb5_prompt kprompt; krb5_prompt_type prompt_type; if (cctx->prompter == NULL) return (EINVAL); kprompt.prompt = prompt; kprompt.hidden = hidden; kprompt.reply = reply; /* * Note, assuming this type for now, may need to be passed in in the future. */ prompt_type = KRB5_PROMPT_TYPE_PREAUTH; /* PROMPTER_INVOCATION */ k5int_set_prompt_types(context, &prompt_type); r = (*cctx->prompter)(context, cctx->prompter_data, NULL, NULL, 1, &kprompt); k5int_set_prompt_types(context, NULL); return (r); } /* * Solaris Kerberos: this function was changed to support a PIN being passed * in. If that is the case the user will not be prompted for their PIN. */ static krb5_error_code pkinit_login(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, CK_TOKEN_INFO *tip) { krb5_data rdat; char *prompt; int prompt_len; int r = 0; if (tip->flags & CKF_PROTECTED_AUTHENTICATION_PATH) { rdat.data = NULL; rdat.length = 0; } else if (id_cryptoctx->PIN != NULL) { if ((rdat.data = strdup(id_cryptoctx->PIN)) == NULL) return (ENOMEM); /* * Don't include NULL string terminator in length calculation as this * PIN is passed to the C_Login function and only the text chars should * be considered to be the PIN. */ rdat.length = strlen(id_cryptoctx->PIN); } else { /* Solaris Kerberos - trim token label */ char tmplabel[sizeof (tip->label) + 1]; if (!id_cryptoctx->prompter) { pkiDebug("pkinit_login: id_cryptoctx->prompter is NULL\n"); /* Solaris Kerberos: Improved error messages */ krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, gettext("Failed to log into token: prompter function is NULL")); return (KRB5KDC_ERR_PREAUTH_FAILED); } /* Solaris Kerberos - Changes for gettext() */ prompt_len = sizeof (tip->label) + 256; if ((prompt = (char *) malloc(prompt_len)) == NULL) return ENOMEM; /* Solaris Kerberos - trim token label which can be padded with space */ trim_token_label(tip, tmplabel, sizeof (tmplabel)); (void) snprintf(prompt, prompt_len, gettext("%s PIN"), tmplabel); /* Solaris Kerberos */ if (tip->flags & CKF_USER_PIN_LOCKED) (void) strlcat(prompt, gettext(" (Warning: PIN locked)"), prompt_len); else if (tip->flags & CKF_USER_PIN_FINAL_TRY) (void) strlcat(prompt, gettext(" (Warning: PIN final try)"), prompt_len); else if (tip->flags & CKF_USER_PIN_COUNT_LOW) (void) strlcat(prompt, gettext(" (Warning: PIN count low)"), prompt_len); rdat.data = malloc(tip->ulMaxPinLen + 2); rdat.length = tip->ulMaxPinLen + 1; /* * Note that the prompter function will set rdat.length such that the * NULL terminator is not included */ /* PROMPTER_INVOCATION */ r = pkinit_prompt_user(context, id_cryptoctx, &rdat, prompt, 1); free(prompt); } if (r == 0) { r = id_cryptoctx->p11->C_Login(id_cryptoctx->session, CKU_USER, (u_char *) rdat.data, rdat.length); if (r != CKR_OK) { pkiDebug("C_Login: %s\n", pkinit_pkcs11_code_to_text(r)); /* Solaris Kerberos: Improved error messages */ krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, gettext("Failed to log into token: %s"), pkinit_pkcs11_code_to_text(r)); r = KRB5KDC_ERR_PREAUTH_FAILED; } else { /* Solaris Kerberos: only need to login once */ id_cryptoctx->p11flags |= C_LOGIN_DONE; } } if (rdat.data) { (void) memset(rdat.data, 0, rdat.length); free(rdat.data); } return (r); } /* * Solaris Kerberos: added these structs in support of prompting user for * missing token. */ struct _token_entry { CK_SLOT_ID slotID; CK_SESSION_HANDLE session; CK_TOKEN_INFO token_info; }; struct _token_choices { unsigned int numtokens; struct _token_entry *token_array; }; /* * Solaris Kerberos: this is a new function that does not exist yet in the MIT * code. */ static krb5_error_code pkinit_prompt_token(krb5_context context, pkinit_identity_crypto_context cctx) { char tmpbuf[4]; krb5_data reply; char *token_prompt = gettext("If you have a smartcard insert it now. " "Press enter to continue"); reply.data = tmpbuf; reply.length = sizeof(tmpbuf); /* note, don't care about the reply */ return (pkinit_prompt_user(context, cctx, &reply, token_prompt, 0)); } /* * Solaris Kerberos: new defines for prompting support. */ #define CHOOSE_THIS_TOKEN 0 #define CHOOSE_RESCAN 1 #define CHOOSE_SKIP 2 #define CHOOSE_SEE_NEXT 3 #define RESCAN_TOKENS -1 #define SKIP_TOKENS -2 /* * Solaris Kerberos: this is a new function that does not exist yet in the MIT * code. * * This prompts to user for various choices regarding a token to use. Note * that if there is no error, choice will be set to one of: * - the token_choices->token_array entry * - RESCAN_TOKENS * - SKIP_TOKENS */ static int pkinit_choose_tokens(krb5_context context, pkinit_identity_crypto_context cctx, struct _token_choices *token_choices, int *choice) { krb5_error_code r; /* * Assuming that PAM_MAX_MSG_SIZE is a reasonable restriction. Note that - * 2 is to account for the fact that a krb prompter to PAM conv bridge will * add ": ". */ char prompt[PAM_MAX_MSG_SIZE - 2]; char tmpbuf[4]; char tmplabel[sizeof (token_choices->token_array->token_info.label) + 1]; krb5_data reply; int i, num_used, tmpchoice; assert(token_choices != NULL); assert(choice != NULL); /* Create the menu prompt */ /* only need to do this once before the for loop */ reply.data = tmpbuf; for (i = 0; i < token_choices->numtokens; i++) { trim_token_label(&token_choices->token_array[i].token_info, tmplabel, sizeof (tmplabel)); if (i == (token_choices->numtokens - 1)) { /* no more smartcards/tokens */ if ((num_used = snprintf(prompt, sizeof (prompt), "%s\n%d: %s \"%s\" %s %d\n%d: %s\n%d: %s\n", /* * TRANSLATION_NOTE: Translations of the * following 5 strings must not exceed 450 * bytes total. */ gettext("Select one of the following and press enter:"), CHOOSE_THIS_TOKEN, gettext("Use smartcard"), tmplabel, gettext("in slot"), token_choices->token_array[i].slotID, CHOOSE_RESCAN, gettext("Rescan for newly inserted smartcard"), CHOOSE_SKIP, gettext("Skip smartcard authentication"))) >= sizeof (prompt)) { pkiDebug("pkinit_choose_tokens: buffer overflow num_used: %d," " sizeof prompt: %d\n", num_used, sizeof (prompt)); krb5_set_error_message(context, EINVAL, gettext("In pkinit_choose_tokens: prompt size" " %d exceeds prompt buffer size %d"), num_used, sizeof(prompt)); (void) snprintf(prompt, sizeof (prompt), "%s", gettext("Error: PKINIT prompt message is too large for buffer, " "please alert the system administrator. Press enter to " "continue")); reply.length = sizeof(tmpbuf); if ((r = pkinit_prompt_user(context, cctx, &reply, prompt, 0)) != 0 ) return (r); return (EINVAL); } } else { if ((num_used = snprintf(prompt, sizeof (prompt), "%s\n%d: %s \"%s\" %s %d\n%d: %s\n%d: %s\n%d: %s\n", /* * TRANSLATION_NOTE: Translations of the * following 6 strings must not exceed 445 * bytes total. */ gettext("Select one of the following and press enter:"), CHOOSE_THIS_TOKEN, gettext("Use smartcard"), tmplabel, gettext("in slot"), token_choices->token_array[i].slotID, CHOOSE_RESCAN, gettext("Rescan for newly inserted smartcard"), CHOOSE_SKIP, gettext("Skip smartcard authentication"), CHOOSE_SEE_NEXT, gettext("See next smartcard"))) >= sizeof (prompt)) { pkiDebug("pkinit_choose_tokens: buffer overflow num_used: %d," " sizeof prompt: %d\n", num_used, sizeof (prompt)); krb5_set_error_message(context, EINVAL, gettext("In pkinit_choose_tokens: prompt size" " %d exceeds prompt buffer size %d"), num_used, sizeof(prompt)); (void) snprintf(prompt, sizeof (prompt), "%s", gettext("Error: PKINIT prompt message is too large for buffer, " "please alert the system administrator. Press enter to " "continue")); reply.length = sizeof(tmpbuf); if ((r = pkinit_prompt_user(context, cctx, &reply, prompt, 0)) != 0 ) return (r); return (EINVAL); } } /* * reply.length needs to be reset to length of tmpbuf before calling * prompter */ reply.length = sizeof(tmpbuf); if ((r = pkinit_prompt_user(context, cctx, &reply, prompt, 0)) != 0 ) return (r); if (reply.length == 0) { return (EINVAL); } else { char *cp = reply.data; /* reply better be digits */ while (*cp != '\0') { if (!isdigit(*cp++)) return (EINVAL); } errno = 0; tmpchoice = (int) strtol(reply.data, (char **)NULL, 10); if (errno != 0) return (errno); } switch (tmpchoice) { case CHOOSE_THIS_TOKEN: *choice = i; /* chosen entry of token_choices->token_array */ return (0); case CHOOSE_RESCAN: *choice = RESCAN_TOKENS; /* rescan for new smartcard */ return (0); case CHOOSE_SKIP: *choice = SKIP_TOKENS; /* skip smartcard auth */ return (0); case CHOOSE_SEE_NEXT: /* see next smartcard */ continue; default: return (EINVAL); } } return (0); } /* * Solaris Kerberos: this is a new function that does not exist yet in the MIT * code. * * Note, this isn't the best solution to providing a function to check the * certs in a token however I wanted to avoid rewriting a bunch of code so I * settled for some duplication of processing. */ static krb5_error_code check_load_certs(krb5_context context, CK_SESSION_HANDLE session, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_principal princ, int do_matching, int load_cert) { CK_OBJECT_CLASS cls; CK_OBJECT_HANDLE obj; CK_ATTRIBUTE attrs[4]; CK_ULONG count; CK_CERTIFICATE_TYPE certtype; CK_BYTE_PTR cert = NULL, cert_id = NULL; const unsigned char *cp; int i, r; unsigned int nattrs; X509 *x = NULL; cls = CKO_CERTIFICATE; attrs[0].type = CKA_CLASS; attrs[0].pValue = &cls; attrs[0].ulValueLen = sizeof cls; certtype = CKC_X_509; attrs[1].type = CKA_CERTIFICATE_TYPE; attrs[1].pValue = &certtype; attrs[1].ulValueLen = sizeof certtype; nattrs = 2; /* If a cert id and/or label were given, use them too */ if (id_cryptoctx->cert_id_len > 0) { attrs[nattrs].type = CKA_ID; attrs[nattrs].pValue = id_cryptoctx->cert_id; attrs[nattrs].ulValueLen = id_cryptoctx->cert_id_len; nattrs++; } if (id_cryptoctx->cert_label != NULL) { attrs[nattrs].type = CKA_LABEL; attrs[nattrs].pValue = id_cryptoctx->cert_label; attrs[nattrs].ulValueLen = strlen(id_cryptoctx->cert_label); nattrs++; } r = id_cryptoctx->p11->C_FindObjectsInit(session, attrs, nattrs); if (r != CKR_OK) { pkiDebug("C_FindObjectsInit: %s\n", pkinit_pkcs11_code_to_text(r)); krb5_set_error_message(context, EINVAL, gettext("PKCS11 error from C_FindObjectsInit: %s"), pkinit_pkcs11_code_to_text(r)); r = EINVAL; goto out; } for (i = 0; ; i++) { if (i >= MAX_CREDS_ALLOWED) { r = EINVAL; goto out; } /* Look for x.509 cert */ /* Solaris Kerberos */ if ((r = id_cryptoctx->p11->C_FindObjects(session, &obj, 1, &count)) != CKR_OK || count == 0) { id_cryptoctx->creds[i] = NULL; break; } /* Get cert and id len */ attrs[0].type = CKA_VALUE; attrs[0].pValue = NULL; attrs[0].ulValueLen = 0; attrs[1].type = CKA_ID; attrs[1].pValue = NULL; attrs[1].ulValueLen = 0; if ((r = id_cryptoctx->p11->C_GetAttributeValue(session, obj, attrs, 2)) != CKR_OK && r != CKR_BUFFER_TOO_SMALL) { pkiDebug("C_GetAttributeValue: %s\n", pkinit_pkcs11_code_to_text(r)); krb5_set_error_message(context, EINVAL, gettext("Error from PKCS11 C_GetAttributeValue: %s"), pkinit_pkcs11_code_to_text(r)); r = EINVAL; goto out; } cert = malloc((size_t) attrs[0].ulValueLen + 1); if (cert == NULL) { r = ENOMEM; goto out; } cert_id = malloc((size_t) attrs[1].ulValueLen + 1); if (cert_id == NULL) { r = ENOMEM; goto out; } /* Read the cert and id off the card */ attrs[0].type = CKA_VALUE; attrs[0].pValue = cert; attrs[1].type = CKA_ID; attrs[1].pValue = cert_id; if ((r = id_cryptoctx->p11->C_GetAttributeValue(session, obj, attrs, 2)) != CKR_OK) { pkiDebug("C_GetAttributeValue: %s\n", pkinit_pkcs11_code_to_text(r)); krb5_set_error_message(context, EINVAL, gettext("Error from PKCS11 C_GetAttributeValue: %s"), pkinit_pkcs11_code_to_text(r)); r = EINVAL; goto out; } pkiDebug("cert %d size %d id %d idlen %d\n", i, (int) attrs[0].ulValueLen, (int) cert_id[0], (int) attrs[1].ulValueLen); cp = (unsigned char *) cert; x = d2i_X509(NULL, &cp, (int) attrs[0].ulValueLen); if (x == NULL) { r = EINVAL; goto out; } id_cryptoctx->creds[i] = malloc(sizeof(struct _pkinit_cred_info)); if (id_cryptoctx->creds[i] == NULL) { r = ENOMEM; goto out; } id_cryptoctx->creds[i]->cert = x; id_cryptoctx->creds[i]->key = NULL; id_cryptoctx->creds[i]->cert_id = cert_id; cert_id = NULL; id_cryptoctx->creds[i]->cert_id_len = attrs[1].ulValueLen; free(cert); cert = NULL; } id_cryptoctx->p11->C_FindObjectsFinal(session); if (id_cryptoctx->creds[0] == NULL || id_cryptoctx->creds[0]->cert == NULL) { r = ENOENT; } else if (do_matching){ /* * Do not let pkinit_cert_matching set the primary cert in id_cryptoctx * as this will be done later. */ r = pkinit_cert_matching(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, princ, FALSE); } out: if ((r != 0 || !load_cert) && id_cryptoctx->creds[0] != NULL && id_cryptoctx->creds[0]->cert != NULL) { /* * If there's an error or load_cert isn't 1 free all the certs loaded * onto id_cryptoctx. */ (void) crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx); } if (cert) free(cert); if (cert_id) free(cert_id); return (r); } /* * Solaris Kerberos: this function has been significantly modified to prompt * the user in certain cases so defer to this version when resyncing MIT code. * * pkinit_open_session now does several things including prompting the user if * do_matching is set which indicates the code is executing in a client * context. This function fills out a pkinit_identity_crypto_context with a * set of certs and a open session if a token can be found that matches all * supplied criteria. If no token is found then the user is prompted one time * to insert their token. If there is more than one token that matches all * client criteria the user is prompted to make a choice if in client context. * If do_matching is false (KDC context) then the first token matching all * server criteria is chosen. */ static krb5_error_code pkinit_open_session(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context cctx, krb5_principal princ, int do_matching) { int i, r; CK_ULONG count = 0; CK_SLOT_ID_PTR slotlist = NULL, tmpslotlist = NULL; CK_TOKEN_INFO tinfo; krb5_boolean tokenmatch = FALSE; CK_SESSION_HANDLE tmpsession = CK_INVALID_HANDLE; struct _token_choices token_choices; int choice = 0; if (cctx->session != CK_INVALID_HANDLE) return 0; /* session already open */ /* Load module */ if (cctx->p11_module == NULL) { cctx->p11_module = pkinit_C_LoadModule(cctx->p11_module_name, &cctx->p11); if (cctx->p11_module == NULL) return KRB5KDC_ERR_PREAUTH_FAILED; } /* Init */ /* Solaris Kerberos: Don't fail if cryptoki is already initialized */ r = cctx->p11->C_Initialize(NULL); if (r != CKR_OK && r != CKR_CRYPTOKI_ALREADY_INITIALIZED) { pkiDebug("C_Initialize: %s\n", pkinit_pkcs11_code_to_text(r)); krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, gettext("Error from PKCS11 C_Initialize: %s"), pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } (void) memset(&token_choices, 0, sizeof(token_choices)); /* * Solaris Kerberos: * If C_Initialize was already called by the process before the pkinit * module was loaded then record that fact. * "finalize_pkcs11" is used by pkinit_fini_pkcs11 to determine whether * or not C_Finalize() should be called. */ cctx->finalize_pkcs11 = (r == CKR_CRYPTOKI_ALREADY_INITIALIZED ? FALSE : TRUE); /* * First make sure that is an applicable slot otherwise fail. * * Start by getting a count of all slots with or without tokens. */ if ((r = cctx->p11->C_GetSlotList(FALSE, NULL, &count)) != CKR_OK) { pkiDebug("C_GetSlotList: %s\n", pkinit_pkcs11_code_to_text(r)); krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, gettext("Error trying to get PKCS11 slot list: %s"), pkinit_pkcs11_code_to_text(r)); r = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } if (count == 0) { /* There are no slots so bail */ r = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, r, gettext("No PKCS11 slots found")); pkiDebug("pkinit_open_session: no slots, count: %d\n", count); goto out; } else if (cctx->slotid != PK_NOSLOT) { /* See if any of the slots match the specified slotID */ tmpslotlist = malloc(count * sizeof (CK_SLOT_ID)); if (tmpslotlist == NULL) { krb5_set_error_message(context, ENOMEM, gettext("Memory allocation error:")); r = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } if ((r = cctx->p11->C_GetSlotList(FALSE, tmpslotlist, &count)) != CKR_OK) { krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, gettext("Error trying to get PKCS11 slot list: %s"), pkinit_pkcs11_code_to_text(r)); pkiDebug("C_GetSlotList: %s\n", pkinit_pkcs11_code_to_text(r)); r = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } for (i = 0; i < count && cctx->slotid != tmpslotlist[i]; i++) continue; if (i >= count) { /* no slots match */ r = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, r, gettext("Requested PKCS11 slot ID %d not found"), cctx->slotid); pkiDebug("open_session: no matching slot found for slotID %d\n", cctx->slotid); goto out; } } tryagain: /* get count of slots that have tokens */ if ((r = cctx->p11->C_GetSlotList(TRUE, NULL, &count)) != CKR_OK) { pkiDebug("C_GetSlotList: %s\n", pkinit_pkcs11_code_to_text(r)); krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, gettext("Error trying to get PKCS11 slot list: %s"), pkinit_pkcs11_code_to_text(r)); r = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } if (count == 0) { /* * Note, never prompt if !do_matching as this implies KDC side * processing */ if (!(cctx->p11flags & C_PROMPTED_USER) && do_matching) { /* found slot(s) but no token so prompt and try again */ if ((r = pkinit_prompt_token(context, cctx)) == 0) { cctx->p11flags |= C_PROMPTED_USER; goto tryagain; } else { pkiDebug("open_session: prompt for token/smart card failed\n"); krb5_set_error_message(context, r, gettext("Prompt for token/smart card failed")); r = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } } else { /* already prompted once so bailing */ r = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, r, gettext("No smart card tokens found")); pkiDebug("pkinit_open_session: no token, already prompted\n"); goto out; } } if (slotlist != NULL) free(slotlist); slotlist = malloc(count * sizeof (CK_SLOT_ID)); if (slotlist == NULL) { krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, gettext("Memory allocation error")); r = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } /* * Solaris Kerberos: get list of PKCS11 slotid's that have tokens. */ if (cctx->p11->C_GetSlotList(TRUE, slotlist, &count) != CKR_OK) { krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, gettext("Error trying to get PKCS11 slot list: %s"), pkinit_pkcs11_code_to_text(r)); pkiDebug("C_GetSlotList: %s\n", pkinit_pkcs11_code_to_text(r)); r = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } token_choices.numtokens = 0; token_choices.token_array = malloc(count * sizeof (*token_choices.token_array)); if (token_choices.token_array == NULL) { r = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, r, gettext("Memory allocation error")); goto out; } /* examine all the tokens */ for (i = 0; i < count; i++) { /* * Solaris Kerberos: if a slotid was specified skip slots that don't * match. */ if (cctx->slotid != PK_NOSLOT && cctx->slotid != slotlist[i]) continue; /* Open session */ if ((r = cctx->p11->C_OpenSession(slotlist[i], CKF_SERIAL_SESSION, NULL, NULL, &tmpsession)) != CKR_OK) { pkiDebug("C_OpenSession: %s\n", pkinit_pkcs11_code_to_text(r)); krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, gettext("Error trying to open PKCS11 session: %s"), pkinit_pkcs11_code_to_text(r)); r = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } /* Get token info */ if ((r = cctx->p11->C_GetTokenInfo(slotlist[i], &tinfo)) != CKR_OK) { pkiDebug("C_GetTokenInfo: %s\n", pkinit_pkcs11_code_to_text(r)); krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, gettext("Error trying to read PKCS11 token: %s"), pkinit_pkcs11_code_to_text(r)); r = KRB5KDC_ERR_PREAUTH_FAILED; cctx->p11->C_CloseSession(tmpsession); goto out; } if (cctx->token_label == NULL) { /* * If the token doesn't require login to examine the certs then * let's check the certs out to see if any match the criteria if * any. */ if (!(tinfo.flags & CKF_LOGIN_REQUIRED)) { /* * It's okay to check the certs if we don't have to login but * don't load the certs onto cctx at this point, this will be * done later in this function for the chosen token. */ if ((r = check_load_certs(context, tmpsession, plg_cryptoctx, req_cryptoctx, cctx, princ, do_matching, 0)) == 0) { tokenmatch = TRUE; } else if (r != ENOENT){ r = KRB5KDC_ERR_PREAUTH_FAILED; cctx->p11->C_CloseSession(tmpsession); goto out; } else { /* ignore ENOENT here */ r = 0; } } else { tokenmatch = TRUE; } } else { /* + 1 so tokenlabelstr can be \0 terminated */ char tokenlabelstr[sizeof (tinfo.label) + 1]; /* * Convert token label into C string with trailing white space * trimmed. */ trim_token_label(&tinfo, tokenlabelstr, sizeof (tokenlabelstr)); pkiDebug("open_session: slotid %d token found: \"%s\", " "cctx->token_label: \"%s\"\n", slotlist[i], tokenlabelstr, (char *) cctx->token_label); if (!strcmp(cctx->token_label, tokenlabelstr)) { if (!(tinfo.flags & CKF_LOGIN_REQUIRED)) { /* * It's okay to check the certs if we don't have to login but * don't load the certs onto cctx at this point, this will be * done later in this function for the chosen token. */ if ((r = check_load_certs(context, tmpsession, plg_cryptoctx, req_cryptoctx, cctx, princ, do_matching, 0)) == 0) { tokenmatch = TRUE; } else if (r != ENOENT){ r = KRB5KDC_ERR_PREAUTH_FAILED; cctx->p11->C_CloseSession(tmpsession); goto out; } else { /* ignore ENOENT here */ r = 0; } } else { tokenmatch = TRUE; } } } if (tokenmatch == TRUE) { /* add the token to token_choices.token_array */ token_choices.token_array[token_choices.numtokens].slotID = slotlist[i]; token_choices.token_array[token_choices.numtokens].session = tmpsession; token_choices.token_array[token_choices.numtokens].token_info = tinfo; token_choices.numtokens++; /* !do_matching implies we take the first matching token */ if (!do_matching) break; else tokenmatch = FALSE; } else { cctx->p11->C_CloseSession(tmpsession); } } if (token_choices.numtokens == 0) { /* * Solaris Kerberos: prompt for token one time if there was no token * and do_matching is 1 (see earlier comment about do_matching). */ if (!(cctx->p11flags & C_PROMPTED_USER) && do_matching) { if ((r = pkinit_prompt_token(context, cctx)) == 0) { cctx->p11flags |= C_PROMPTED_USER; goto tryagain; } else { pkiDebug("open_session: prompt for token/smart card failed\n"); krb5_set_error_message(context, r, gettext("Prompt for token/smart card failed")); r = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } } else { r = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, r, gettext("No smart card tokens found")); pkiDebug("open_session: no matching token found\n"); goto out; } } else if (token_choices.numtokens == 1) { if ((token_choices.token_array[0].token_info.flags & CKF_LOGIN_REQUIRED) && !(cctx->p11flags & C_PROMPTED_USER) && do_matching) { if ((r = pkinit_choose_tokens(context, cctx, &token_choices, &choice)) != 0) { pkiDebug("pkinit_open_session: pkinit_choose_tokens failed: %d\n", r); r = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, r, gettext("Prompt for token/smart card failed")); goto out; } if (choice == RESCAN_TOKENS) { /* rescan for new smartcard/token */ for (i = 0; i < token_choices.numtokens; i++) { /* close all sessions */ cctx->p11->C_CloseSession(token_choices.token_array[i].session); } free(token_choices.token_array); token_choices.token_array = NULL; token_choices.numtokens = 0; goto tryagain; } else if (choice == SKIP_TOKENS) { /* do not use smartcard/token for auth */ cctx->p11flags |= (C_PROMPTED_USER|C_SKIP_PKCS11_AUTH); r = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } else { cctx->p11flags |= C_PROMPTED_USER; } } else { choice = 0; /* really the only choice is the first token_array entry */ } } else if (!(cctx->p11flags & C_PROMPTED_USER) && do_matching) { /* > 1 token so present menu of token choices, let the user decide. */ if ((r = pkinit_choose_tokens(context, cctx, &token_choices, &choice)) != 0) { pkiDebug("pkinit_open_session: pkinit_choose_tokens failed: %d\n", r); r = KRB5KDC_ERR_PREAUTH_FAILED; krb5_set_error_message(context, r, gettext("Prompt for token/smart card failed")); goto out; } if (choice == RESCAN_TOKENS) { /* rescan for new smartcard/token */ for (i = 0; i < token_choices.numtokens; i++) { /* close all sessions */ cctx->p11->C_CloseSession(token_choices.token_array[i].session); } free(token_choices.token_array); token_choices.token_array = NULL; token_choices.numtokens = 0; goto tryagain; } else if (choice == SKIP_TOKENS) { /* do not use smartcard/token for auth */ cctx->p11flags |= (C_PROMPTED_USER|C_SKIP_PKCS11_AUTH); r = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } else { cctx->p11flags |= C_PROMPTED_USER; } } else { r = KRB5KDC_ERR_PREAUTH_FAILED; goto out; } cctx->slotid = token_choices.token_array[choice].slotID; cctx->session = token_choices.token_array[choice].session; pkiDebug("open_session: slotid %d (%d of %d)\n", (int) cctx->slotid, i + 1, (int) count); /* Login if needed */ /* Solaris Kerberos: added cctx->p11flags check */ if ((token_choices.token_array[choice].token_info.flags & CKF_LOGIN_REQUIRED) && !(cctx->p11flags & C_LOGIN_DONE)) { r = pkinit_login(context, cctx, &token_choices.token_array[choice].token_info); } if (r == 0) { /* Doing this again to load the certs into cctx. */ r = check_load_certs(context, cctx->session, plg_cryptoctx, req_cryptoctx, cctx, princ, do_matching, 1); } out: if (slotlist != NULL) free(slotlist); if (tmpslotlist != NULL) free(tmpslotlist); if (token_choices.token_array != NULL) { if (r != 0) { /* close all sessions if there's an error */ for (i = 0; i < token_choices.numtokens; i++) { cctx->p11->C_CloseSession(token_choices.token_array[i].session); } cctx->session = CK_INVALID_HANDLE; } else { /* close sessions not chosen */ for (i = 0; i < token_choices.numtokens; i++) { if (i != choice) { cctx->p11->C_CloseSession(token_choices.token_array[i].session); } } } free(token_choices.token_array); } return (r); } /* * Look for a key that's: * 1. private * 2. capable of the specified operation (usually signing or decrypting) * 3. RSA (this may be wrong but it's all we can do for now) * 4. matches the id of the cert we chose * * You must call pkinit_get_certs before calling pkinit_find_private_key * (that's because we need the ID of the private key) * * pkcs11 says the id of the key doesn't have to match that of the cert, but * I can't figure out any other way to decide which key to use. * * We should only find one key that fits all the requirements. * If there are more than one, we just take the first one. */ /* ARGSUSED */ krb5_error_code pkinit_find_private_key(pkinit_identity_crypto_context id_cryptoctx, CK_ATTRIBUTE_TYPE usage, CK_OBJECT_HANDLE *objp) { CK_OBJECT_CLASS cls; CK_ATTRIBUTE attrs[4]; CK_ULONG count; CK_KEY_TYPE keytype; #if OPENSSL_VERSION_NUMBER >= 0x30000000L const RSA *rsa; #else RSA *rsa; #endif unsigned int nattrs = 0; int r; #ifdef PKINIT_USE_KEY_USAGE CK_BBOOL true_false; #endif cls = CKO_PRIVATE_KEY; attrs[nattrs].type = CKA_CLASS; attrs[nattrs].pValue = &cls; attrs[nattrs].ulValueLen = sizeof cls; nattrs++; #ifdef PKINIT_USE_KEY_USAGE /* * Some cards get confused if you try to specify a key usage, * so don't, and hope for the best. This will fail if you have * several keys with the same id and different usages but I have * not seen this on real cards. */ true_false = TRUE; attrs[nattrs].type = usage; attrs[nattrs].pValue = &true_false; attrs[nattrs].ulValueLen = sizeof true_false; nattrs++; #endif keytype = CKK_RSA; attrs[nattrs].type = CKA_KEY_TYPE; attrs[nattrs].pValue = &keytype; attrs[nattrs].ulValueLen = sizeof keytype; nattrs++; attrs[nattrs].type = CKA_ID; attrs[nattrs].pValue = id_cryptoctx->cert_id; attrs[nattrs].ulValueLen = id_cryptoctx->cert_id_len; nattrs++; r = id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, attrs, nattrs); if (r != CKR_OK) { pkiDebug("krb5_pkinit_sign_data: C_FindObjectsInit: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } r = id_cryptoctx->p11->C_FindObjects(id_cryptoctx->session, objp, 1, &count); id_cryptoctx->p11->C_FindObjectsFinal(id_cryptoctx->session); pkiDebug("found %d private keys (%s)\n", (int) count, pkinit_pkcs11_code_to_text(r)); /* * Solaris Kerberos: * The CKA_ID may not be correctly set for the private key. For e.g. when * storing a private key in softtoken pktool(1) doesn't generate or store * a CKA_ID for the private key. Another way to identify the private key is * to look for a private key with the same RSA modulus as the public key * in the certificate. */ if (r == CKR_OK && count != 1) { EVP_PKEY *priv; X509 *cert; const BIGNUM *rsan; unsigned int n_len; unsigned char *n_bytes; cert = sk_X509_value(id_cryptoctx->my_certs, 0); priv = X509_get_pubkey(cert); if (priv == NULL) { pkiDebug("Failed to extract pub key from cert\n"); return KRB5KDC_ERR_PREAUTH_FAILED; } nattrs = 0; cls = CKO_PRIVATE_KEY; attrs[nattrs].type = CKA_CLASS; attrs[nattrs].pValue = &cls; attrs[nattrs].ulValueLen = sizeof cls; nattrs++; #ifdef PKINIT_USE_KEY_USAGE true_false = TRUE; attrs[nattrs].type = usage; attrs[nattrs].pValue = &true_false; attrs[nattrs].ulValueLen = sizeof true_false; nattrs++; #endif keytype = CKK_RSA; attrs[nattrs].type = CKA_KEY_TYPE; attrs[nattrs].pValue = &keytype; attrs[nattrs].ulValueLen = sizeof keytype; nattrs++; #if OPENSSL_VERSION_NUMBER < 0x10100000L rsa = priv->pkey.rsa; rsan = rsa->n; n_len = BN_num_bytes(rsan); #else rsa = EVP_PKEY_get0_RSA(priv); RSA_get0_key(rsa, &rsan, NULL, NULL); n_len = RSA_size(rsa); #endif n_bytes = (unsigned char *) malloc((size_t) n_len); if (n_bytes == NULL) { return (ENOMEM); } if (BN_bn2bin(rsan, n_bytes) == 0) { free (n_bytes); pkiDebug("zero-byte key modulus\n"); return KRB5KDC_ERR_PREAUTH_FAILED; } attrs[nattrs].type = CKA_MODULUS; attrs[nattrs].ulValueLen = n_len; attrs[nattrs].pValue = n_bytes; nattrs++; r = id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, attrs, nattrs); free (n_bytes); if (r != CKR_OK) { pkiDebug("krb5_pkinit_sign_data: C_FindObjectsInit: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } r = id_cryptoctx->p11->C_FindObjects(id_cryptoctx->session, objp, 1, &count); id_cryptoctx->p11->C_FindObjectsFinal(id_cryptoctx->session); pkiDebug("found %d private keys (%s)\n", (int) count, pkinit_pkcs11_code_to_text(r)); } if (r != CKR_OK || count < 1) return KRB5KDC_ERR_PREAUTH_FAILED; return 0; } #endif /* ARGSUSED */ static krb5_error_code pkinit_decode_data_fs(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **decoded_data, unsigned int *decoded_data_len) { if (decode_data(decoded_data, decoded_data_len, data, data_len, id_cryptoctx->my_key, sk_X509_value(id_cryptoctx->my_certs, id_cryptoctx->cert_index)) <= 0) { pkiDebug("failed to decode data\n"); return KRB5KDC_ERR_PREAUTH_FAILED; } return 0; } #ifndef WITHOUT_PKCS11 #ifdef SILLYDECRYPT CK_RV pkinit_C_Decrypt(pkinit_identity_crypto_context id_cryptoctx, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { CK_RV rv = CKR_OK; rv = id_cryptoctx->p11->C_Decrypt(id_cryptoctx->session, pEncryptedData, ulEncryptedDataLen, pData, pulDataLen); if (rv == CKR_OK) { pkiDebug("pData %x *pulDataLen %d\n", (int) pData, (int) *pulDataLen); } return rv; } #endif static krb5_error_code pkinit_decode_data_pkcs11(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **decoded_data, unsigned int *decoded_data_len) { CK_OBJECT_HANDLE obj; CK_ULONG len; CK_MECHANISM mech; unsigned char *cp; int r; /* * Solaris Kerberos: assume session is open and libpkcs11 funcs have been * loaded. */ assert(id_cryptoctx->p11 != NULL); /* Solaris Kerberos: Login, if needed, to access private object */ if (!(id_cryptoctx->p11flags & C_LOGIN_DONE)) { CK_TOKEN_INFO tinfo; r = id_cryptoctx->p11->C_GetTokenInfo(id_cryptoctx->slotid, &tinfo); if (r != 0) return r; r = pkinit_login(context, id_cryptoctx, &tinfo); if (r != 0) return r; } r = pkinit_find_private_key(id_cryptoctx, CKA_DECRYPT, &obj); if (r != 0) return r; mech.mechanism = CKM_RSA_PKCS; mech.pParameter = NULL; mech.ulParameterLen = 0; if ((r = id_cryptoctx->p11->C_DecryptInit(id_cryptoctx->session, &mech, obj)) != CKR_OK) { pkiDebug("C_DecryptInit: 0x%x\n", (int) r); return KRB5KDC_ERR_PREAUTH_FAILED; } pkiDebug("data_len = %d\n", data_len); cp = (unsigned char *)malloc((size_t) data_len); if (cp == NULL) return ENOMEM; len = data_len; #ifdef SILLYDECRYPT pkiDebug("session %x edata %x edata_len %d data %x datalen @%x %d\n", (int) id_cryptoctx->session, (int) data, (int) data_len, (int) cp, (int) &len, (int) len); if ((r = pkinit_C_Decrypt(id_cryptoctx, data, (CK_ULONG) data_len, cp, &len)) != CKR_OK) { #else if ((r = id_cryptoctx->p11->C_Decrypt(id_cryptoctx->session, data, (CK_ULONG) data_len, cp, &len)) != CKR_OK) { #endif pkiDebug("C_Decrypt: %s\n", pkinit_pkcs11_code_to_text(r)); if (r == CKR_BUFFER_TOO_SMALL) pkiDebug("decrypt %d needs %d\n", (int) data_len, (int) len); return KRB5KDC_ERR_PREAUTH_FAILED; } pkiDebug("decrypt %d -> %d\n", (int) data_len, (int) len); *decoded_data_len = len; *decoded_data = cp; return 0; } #endif krb5_error_code pkinit_decode_data(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **decoded_data, unsigned int *decoded_data_len) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; if (id_cryptoctx->pkcs11_method != 1) retval = pkinit_decode_data_fs(context, id_cryptoctx, data, data_len, decoded_data, decoded_data_len); #ifndef WITHOUT_PKCS11 else retval = pkinit_decode_data_pkcs11(context, id_cryptoctx, data, data_len, decoded_data, decoded_data_len); #endif return retval; } /* ARGSUSED */ static krb5_error_code pkinit_sign_data_fs(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **sig, unsigned int *sig_len) { if (create_signature(sig, sig_len, data, data_len, id_cryptoctx->my_key) != 0) { pkiDebug("failed to create the signature\n"); return KRB5KDC_ERR_PREAUTH_FAILED; } return 0; } #ifndef WITHOUT_PKCS11 static krb5_error_code pkinit_sign_data_pkcs11(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **sig, unsigned int *sig_len) { CK_OBJECT_HANDLE obj; CK_ULONG len; CK_MECHANISM mech; unsigned char *cp; int r; /* * Solaris Kerberos: assume session is open and libpkcs11 funcs have been * loaded. */ assert(id_cryptoctx->p11 != NULL); /* Solaris Kerberos: Login, if needed, to access private object */ if (!(id_cryptoctx->p11flags & C_LOGIN_DONE)) { CK_TOKEN_INFO tinfo; r = id_cryptoctx->p11->C_GetTokenInfo(id_cryptoctx->slotid, &tinfo); if (r != 0) return r; r = pkinit_login(context, id_cryptoctx, &tinfo); if (r != 0) return r; } r = pkinit_find_private_key(id_cryptoctx, CKA_SIGN, &obj); if (r != 0 ) return r; mech.mechanism = id_cryptoctx->mech; mech.pParameter = NULL; mech.ulParameterLen = 0; if ((r = id_cryptoctx->p11->C_SignInit(id_cryptoctx->session, &mech, obj)) != CKR_OK) { pkiDebug("C_SignInit: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } /* * Key len would give an upper bound on sig size, but there's no way to * get that. So guess, and if it's too small, re-malloc. */ len = PK_SIGLEN_GUESS; cp = (unsigned char *)malloc((size_t) len); if (cp == NULL) return ENOMEM; r = id_cryptoctx->p11->C_Sign(id_cryptoctx->session, data, (CK_ULONG) data_len, cp, &len); if (r == CKR_BUFFER_TOO_SMALL || (r == CKR_OK && len >= PK_SIGLEN_GUESS)) { free(cp); pkiDebug("C_Sign realloc %d\n", (int) len); cp = (unsigned char *)malloc((size_t) len); r = id_cryptoctx->p11->C_Sign(id_cryptoctx->session, data, (CK_ULONG) data_len, cp, &len); } if (r != CKR_OK) { pkiDebug("C_Sign: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } pkiDebug("sign %d -> %d\n", (int) data_len, (int) len); *sig_len = len; *sig = cp; return 0; } #endif krb5_error_code pkinit_sign_data(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **sig, unsigned int *sig_len) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; if (id_cryptoctx == NULL || id_cryptoctx->pkcs11_method != 1) retval = pkinit_sign_data_fs(context, id_cryptoctx, data, data_len, sig, sig_len); #ifndef WITHOUT_PKCS11 else retval = pkinit_sign_data_pkcs11(context, id_cryptoctx, data, data_len, sig, sig_len); #endif return retval; } static krb5_error_code decode_data(unsigned char **out_data, unsigned int *out_data_len, unsigned char *data, unsigned int data_len, EVP_PKEY *pkey, X509 *cert) { /* Solaris Kerberos */ int len; unsigned char *buf = NULL; int buf_len = 0; /* Solaris Kerberos */ if (out_data == NULL || out_data_len == NULL) return EINVAL; if (cert && !X509_check_private_key(cert, pkey)) { pkiDebug("private key does not match certificate\n"); /* Solaris Kerberos */ return EINVAL; } buf_len = EVP_PKEY_size(pkey); buf = (unsigned char *)malloc((size_t) buf_len + 10); if (buf == NULL) return ENOMEM; len = EVP_PKEY_decrypt_old(buf, data, (int)data_len, pkey); if (len <= 0) { pkiDebug("unable to decrypt received data (len=%d)\n", data_len); /* Solaris Kerberos */ free(buf); return KRB5KRB_ERR_GENERIC; } *out_data = buf; *out_data_len = len; return 0; } static krb5_error_code create_signature(unsigned char **sig, unsigned int *sig_len, unsigned char *data, unsigned int data_len, EVP_PKEY *pkey) { krb5_error_code retval = ENOMEM; EVP_MD_CTX *md_ctx; if (pkey == NULL) /* Solaris Kerberos */ return EINVAL; if ((md_ctx = EVP_MD_CTX_new()) == NULL) return EINVAL; EVP_VerifyInit(md_ctx, EVP_sha1()); EVP_SignUpdate(md_ctx, data, data_len); *sig_len = EVP_PKEY_size(pkey); if ((*sig = (unsigned char *) malloc((size_t) *sig_len)) == NULL) goto cleanup; EVP_SignFinal(md_ctx, *sig, sig_len, pkey); retval = 0; cleanup: EVP_MD_CTX_free(md_ctx); return retval; } /* * Note: * This is not the routine the KDC uses to get its certificate. * This routine is intended to be called by the client * to obtain the KDC's certificate from some local storage * to be sent as a hint in its request to the KDC. */ /* ARGSUSED */ krb5_error_code pkinit_get_kdc_cert(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_principal princ) { /* Solaris Kerberos */ if (req_cryptoctx == NULL) return EINVAL; req_cryptoctx->received_cert = NULL; return 0; } /* ARGSUSED */ static krb5_error_code pkinit_get_certs_pkcs12(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_opts *idopts, pkinit_identity_crypto_context id_cryptoctx, krb5_principal princ) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; X509 *x = NULL; PKCS12 *p12 = NULL; int ret; FILE *fp; EVP_PKEY *y = NULL; if (idopts->cert_filename == NULL) { /* Solaris Kerberos: Improved error messages */ krb5_set_error_message(context, retval, gettext("Failed to get certificate location")); pkiDebug("%s: failed to get user's cert location\n", __FUNCTION__); goto cleanup; } if (idopts->key_filename == NULL) { /* Solaris Kerberos: Improved error messages */ krb5_set_error_message(context, retval, gettext("Failed to get private key location")); pkiDebug("%s: failed to get user's private key location\n", __FUNCTION__); goto cleanup; } fp = fopen(idopts->cert_filename, "rb"); if (fp == NULL) { /* Solaris Kerberos: Improved error messages */ krb5_set_error_message(context, retval, gettext("Failed to open PKCS12 file '%s': %s"), idopts->cert_filename, error_message(errno)); pkiDebug("Failed to open PKCS12 file '%s', error %d\n", idopts->cert_filename, errno); goto cleanup; } p12 = d2i_PKCS12_fp(fp, NULL); (void) fclose(fp); if (p12 == NULL) { krb5_set_error_message(context, retval, gettext("Failed to decode PKCS12 file '%s' contents"), idopts->cert_filename); pkiDebug("Failed to decode PKCS12 file '%s' contents\n", idopts->cert_filename); goto cleanup; } /* * Try parsing with no pass phrase first. If that fails, * prompt for the pass phrase and try again. */ ret = PKCS12_parse(p12, NULL, &y, &x, NULL); if (ret == 0) { krb5_data rdat; krb5_prompt kprompt; krb5_prompt_type prompt_type; int r = 0; char prompt_string[128]; char prompt_reply[128]; /* Solaris Kerberos */ char *prompt_prefix = gettext("Pass phrase for"); pkiDebug("Initial PKCS12_parse with no password failed\n"); if (id_cryptoctx->PIN != NULL) { /* Solaris Kerberos: use PIN if set */ rdat.data = id_cryptoctx->PIN; /* note rdat.length isn't needed in this case */ } else { (void) memset(prompt_reply, '\0', sizeof(prompt_reply)); rdat.data = prompt_reply; rdat.length = sizeof(prompt_reply); r = snprintf(prompt_string, sizeof(prompt_string), "%s %s", prompt_prefix, idopts->cert_filename); if (r >= sizeof(prompt_string)) { pkiDebug("Prompt string, '%s %s', is too long!\n", prompt_prefix, idopts->cert_filename); goto cleanup; } kprompt.prompt = prompt_string; kprompt.hidden = 1; kprompt.reply = &rdat; prompt_type = KRB5_PROMPT_TYPE_PREAUTH; /* PROMPTER_INVOCATION */ k5int_set_prompt_types(context, &prompt_type); r = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data, NULL, NULL, 1, &kprompt); k5int_set_prompt_types(context, NULL); } ret = PKCS12_parse(p12, rdat.data, &y, &x, NULL); if (ret == 0) { /* Solaris Kerberos: Improved error messages */ krb5_set_error_message(context, retval, gettext("Failed to parse PKCS12 file '%s' with password"), idopts->cert_filename); pkiDebug("Seconde PKCS12_parse with password failed\n"); goto cleanup; } } id_cryptoctx->creds[0] = malloc(sizeof(struct _pkinit_cred_info)); if (id_cryptoctx->creds[0] == NULL) goto cleanup; id_cryptoctx->creds[0]->cert = x; #ifndef WITHOUT_PKCS11 id_cryptoctx->creds[0]->cert_id = NULL; id_cryptoctx->creds[0]->cert_id_len = 0; #endif id_cryptoctx->creds[0]->key = y; id_cryptoctx->creds[1] = NULL; retval = 0; cleanup: if (p12) PKCS12_free(p12); if (retval) { if (x != NULL) X509_free(x); if (y != NULL) EVP_PKEY_free(y); } return retval; } static krb5_error_code pkinit_load_fs_cert_and_key(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, char *certname, char *keyname, int cindex) { krb5_error_code retval; X509 *x = NULL; EVP_PKEY *y = NULL; /* load the certificate */ retval = get_cert(certname, &x); if (retval != 0 || x == NULL) { /* Solaris Kerberos: Improved error messages */ krb5_set_error_message(context, retval, gettext("Failed to load user's certificate from %s: %s"), certname, error_message(retval)); pkiDebug("failed to load user's certificate from '%s'\n", certname); goto cleanup; } retval = get_key(keyname, &y); if (retval != 0 || y == NULL) { /* Solaris Kerberos: Improved error messages */ krb5_set_error_message(context, retval, gettext("Failed to load user's private key from %s: %s"), keyname, error_message(retval)); pkiDebug("failed to load user's private key from '%s'\n", keyname); goto cleanup; } id_cryptoctx->creds[cindex] = malloc(sizeof(struct _pkinit_cred_info)); if (id_cryptoctx->creds[cindex] == NULL) { retval = ENOMEM; goto cleanup; } id_cryptoctx->creds[cindex]->cert = x; #ifndef WITHOUT_PKCS11 id_cryptoctx->creds[cindex]->cert_id = NULL; id_cryptoctx->creds[cindex]->cert_id_len = 0; #endif id_cryptoctx->creds[cindex]->key = y; id_cryptoctx->creds[cindex+1] = NULL; retval = 0; cleanup: if (retval) { if (x != NULL) X509_free(x); if (y != NULL) EVP_PKEY_free(y); } return retval; } /* ARGSUSED */ static krb5_error_code pkinit_get_certs_fs(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_opts *idopts, pkinit_identity_crypto_context id_cryptoctx, krb5_principal princ) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; if (idopts->cert_filename == NULL) { pkiDebug("%s: failed to get user's cert location\n", __FUNCTION__); goto cleanup; } if (idopts->key_filename == NULL) { pkiDebug("%s: failed to get user's private key location\n", __FUNCTION__); goto cleanup; } retval = pkinit_load_fs_cert_and_key(context, id_cryptoctx, idopts->cert_filename, idopts->key_filename, 0); cleanup: return retval; } /* ARGSUSED */ static krb5_error_code pkinit_get_certs_dir(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_opts *idopts, pkinit_identity_crypto_context id_cryptoctx, krb5_principal princ) { /* Solaris Kerberos */ krb5_error_code retval = KRB5KRB_ERR_GENERIC; DIR *d = NULL; struct dirent *dentry = NULL; char certname[1024]; char keyname[1024]; int i = 0, len; char *dirname, *suf; /* Solaris Kerberos */ if (idopts == NULL) return EINVAL; if (idopts->cert_filename == NULL) { pkiDebug("%s: failed to get user's certificate directory location\n", __FUNCTION__); return ENOENT; } dirname = idopts->cert_filename; d = opendir(dirname); if (d == NULL) { /* Solaris Kerberos: Improved error messages */ krb5_set_error_message(context, errno, gettext("Failed to open directory \"%s\": %s"), dirname, error_message(errno)); return errno; } /* * We'll assume that certs are named XXX.crt and the corresponding * key is named XXX.key */ while ((i < MAX_CREDS_ALLOWED) && (dentry = readdir(d)) != NULL) { /* Ignore subdirectories and anything starting with a dot */ #ifdef DT_DIR if (dentry->d_type == DT_DIR) continue; #endif if (dentry->d_name[0] == '.') continue; len = strlen(dentry->d_name); if (len < 5) continue; suf = dentry->d_name + (len - 4); if (strncmp(suf, ".crt", 4) != 0) continue; /* Checked length */ if (strlen(dirname) + strlen(dentry->d_name) + 2 > sizeof(certname)) { pkiDebug("%s: Path too long -- directory '%s' and file '%s'\n", __FUNCTION__, dirname, dentry->d_name); continue; } (void) snprintf(certname, sizeof(certname), "%s/%s", dirname, dentry->d_name); (void) snprintf(keyname, sizeof(keyname), "%s/%s", dirname, dentry->d_name); len = strlen(keyname); keyname[len - 3] = 'k'; keyname[len - 2] = 'e'; keyname[len - 1] = 'y'; retval = pkinit_load_fs_cert_and_key(context, id_cryptoctx, certname, keyname, i); if (retval == 0) { pkiDebug("%s: Successfully loaded cert (and key) for %s\n", __FUNCTION__, dentry->d_name); i++; } else continue; } if (i == 0) { /* Solaris Kerberos: Improved error messages */ krb5_set_error_message(context, ENOENT, gettext("No suitable cert/key pairs found in directory '%s'"), idopts->cert_filename); pkiDebug("%s: No cert/key pairs found in directory '%s'\n", __FUNCTION__, idopts->cert_filename); retval = ENOENT; goto cleanup; } retval = 0; cleanup: if (d) (void) closedir(d); return retval; } #ifndef WITHOUT_PKCS11 /* ARGSUSED */ static krb5_error_code pkinit_get_certs_pkcs11(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_opts *idopts, pkinit_identity_crypto_context id_cryptoctx, krb5_principal princ, int do_matching) { #ifdef PKINIT_USE_MECH_LIST CK_MECHANISM_TYPE_PTR mechp = NULL; CK_MECHANISM_INFO info; #endif if (id_cryptoctx->p11flags & C_SKIP_PKCS11_AUTH) return KRB5KDC_ERR_PREAUTH_FAILED; /* Copy stuff from idopts -> id_cryptoctx */ if (idopts->p11_module_name != NULL) { id_cryptoctx->p11_module_name = strdup(idopts->p11_module_name); if (id_cryptoctx->p11_module_name == NULL) return ENOMEM; } if (idopts->token_label != NULL) { id_cryptoctx->token_label = strdup(idopts->token_label); if (id_cryptoctx->token_label == NULL) return ENOMEM; } if (idopts->cert_label != NULL) { id_cryptoctx->cert_label = strdup(idopts->cert_label); if (id_cryptoctx->cert_label == NULL) return ENOMEM; } if (idopts->PIN != NULL) { id_cryptoctx->PIN = strdup(idopts->PIN); if (id_cryptoctx->PIN == NULL) return ENOMEM; } /* Convert the ascii cert_id string into a binary blob */ /* * Solaris Kerberos: * If the cert_id_string is empty then behave in a similar way to how * an empty certlabel is treated - i.e. don't fail now but rather continue * as though the certid wasn't specified. */ if (idopts->cert_id_string != NULL && strlen(idopts->cert_id_string) != 0) { BIGNUM *bn = NULL; BN_hex2bn(&bn, idopts->cert_id_string); if (bn == NULL) return ENOMEM; id_cryptoctx->cert_id_len = BN_num_bytes(bn); id_cryptoctx->cert_id = malloc((size_t) id_cryptoctx->cert_id_len); if (id_cryptoctx->cert_id == NULL) { BN_free(bn); return ENOMEM; } BN_bn2bin(bn, id_cryptoctx->cert_id); BN_free(bn); } id_cryptoctx->slotid = idopts->slotid; id_cryptoctx->pkcs11_method = 1; #ifndef PKINIT_USE_MECH_LIST /* * We'd like to use CKM_SHA1_RSA_PKCS for signing if it's available, but * many cards seems to be confused about whether they are capable of * this or not. The safe thing seems to be to ignore the mechanism list, * always use CKM_RSA_PKCS and calculate the sha1 digest ourselves. */ id_cryptoctx->mech = CKM_RSA_PKCS; #else if ((r = id_cryptoctx->p11->C_GetMechanismList(id_cryptoctx->slotid, NULL, &count)) != CKR_OK || count <= 0) { pkiDebug("C_GetMechanismList: %s\n", pkinit_pkcs11_code_to_text(r)); return KRB5KDC_ERR_PREAUTH_FAILED; } mechp = (CK_MECHANISM_TYPE_PTR) malloc(count * sizeof (CK_MECHANISM_TYPE)); if (mechp == NULL) return ENOMEM; if ((r = id_cryptoctx->p11->C_GetMechanismList(id_cryptoctx->slotid, mechp, &count)) != CKR_OK) { free(mechp); return KRB5KDC_ERR_PREAUTH_FAILED; } for (i = 0; i < count; i++) { if ((r = id_cryptoctx->p11->C_GetMechanismInfo(id_cryptoctx->slotid, mechp[i], &info)) != CKR_OK) { free(mechp); return KRB5KDC_ERR_PREAUTH_FAILED; } #ifdef DEBUG_MECHINFO pkiDebug("mech %x flags %x\n", (int) mechp[i], (int) info.flags); if ((info.flags & (CKF_SIGN|CKF_DECRYPT)) == (CKF_SIGN|CKF_DECRYPT)) pkiDebug(" this mech is good for sign & decrypt\n"); #endif if (mechp[i] == CKM_RSA_PKCS) { /* This seems backwards... */ id_cryptoctx->mech = (info.flags & CKF_SIGN) ? CKM_SHA1_RSA_PKCS : CKM_RSA_PKCS; } } free(mechp); pkiDebug("got %d mechs from card\n", (int) count); #endif return (pkinit_open_session(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, princ, do_matching)); } #endif /* ARGSUSED */ static void free_cred_info(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, struct _pkinit_cred_info *cred) { if (cred != NULL) { if (cred->cert != NULL) X509_free(cred->cert); if (cred->key != NULL) EVP_PKEY_free(cred->key); #ifndef WITHOUT_PKCS11 if (cred->cert_id != NULL) free(cred->cert_id); #endif free(cred); } } /* ARGSUSED */ krb5_error_code crypto_free_cert_info(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx) { int i; if (id_cryptoctx == NULL) return EINVAL; for (i = 0; i < MAX_CREDS_ALLOWED; i++) { if (id_cryptoctx->creds[i] != NULL) { free_cred_info(context, id_cryptoctx, id_cryptoctx->creds[i]); id_cryptoctx->creds[i] = NULL; } } return 0; } krb5_error_code crypto_load_certs(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_opts *idopts, pkinit_identity_crypto_context id_cryptoctx, krb5_principal princ, int do_matching) { krb5_error_code retval; switch(idopts->idtype) { case IDTYPE_FILE: retval = pkinit_get_certs_fs(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, princ); break; case IDTYPE_DIR: retval = pkinit_get_certs_dir(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, princ); break; #ifndef WITHOUT_PKCS11 case IDTYPE_PKCS11: retval = pkinit_get_certs_pkcs11(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, princ, do_matching); break; #endif case IDTYPE_PKCS12: retval = pkinit_get_certs_pkcs12(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, princ); break; default: retval = EINVAL; } /* Solaris Kerberos */ return retval; } /* * Get number of certificates available after crypto_load_certs() */ /* ARGSUSED */ krb5_error_code crypto_cert_get_count(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, int *cert_count) { int count; if (id_cryptoctx == NULL || id_cryptoctx->creds[0] == NULL) return EINVAL; for (count = 0; count <= MAX_CREDS_ALLOWED && id_cryptoctx->creds[count] != NULL; count++); *cert_count = count; return 0; } /* * Begin iteration over the certs loaded in crypto_load_certs() */ /* ARGSUSED */ krb5_error_code crypto_cert_iteration_begin(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, pkinit_cert_iter_handle *ih_ret) { struct _pkinit_cert_iter_data *id; if (id_cryptoctx == NULL || ih_ret == NULL) return EINVAL; if (id_cryptoctx->creds[0] == NULL) /* No cred info available */ return ENOENT; id = calloc(1, sizeof(*id)); if (id == NULL) return ENOMEM; id->magic = ITER_MAGIC; id->plgctx = plg_cryptoctx, id->reqctx = req_cryptoctx, id->idctx = id_cryptoctx; id->index = 0; *ih_ret = (pkinit_cert_iter_handle) id; return 0; } /* * End iteration over the certs loaded in crypto_load_certs() */ /* ARGSUSED */ krb5_error_code crypto_cert_iteration_end(krb5_context context, pkinit_cert_iter_handle ih) { struct _pkinit_cert_iter_data *id = (struct _pkinit_cert_iter_data *)ih; if (id == NULL || id->magic != ITER_MAGIC) return EINVAL; free(ih); return 0; } /* * Get next certificate handle */ /* ARGSUSED */ krb5_error_code crypto_cert_iteration_next(krb5_context context, pkinit_cert_iter_handle ih, pkinit_cert_handle *ch_ret) { struct _pkinit_cert_iter_data *id = (struct _pkinit_cert_iter_data *)ih; struct _pkinit_cert_data *cd; pkinit_identity_crypto_context id_cryptoctx; if (id == NULL || id->magic != ITER_MAGIC) return EINVAL; if (ch_ret == NULL) return EINVAL; id_cryptoctx = id->idctx; if (id_cryptoctx == NULL) return EINVAL; if (id_cryptoctx->creds[id->index] == NULL) return PKINIT_ITER_NO_MORE; cd = calloc(1, sizeof(*cd)); if (cd == NULL) return ENOMEM; cd->magic = CERT_MAGIC; cd->plgctx = id->plgctx; cd->reqctx = id->reqctx; cd->idctx = id->idctx; cd->index = id->index; cd->cred = id_cryptoctx->creds[id->index++]; *ch_ret = (pkinit_cert_handle)cd; return 0; } /* * Release cert handle */ /* ARGSUSED */ krb5_error_code crypto_cert_release(krb5_context context, pkinit_cert_handle ch) { struct _pkinit_cert_data *cd = (struct _pkinit_cert_data *)ch; if (cd == NULL || cd->magic != CERT_MAGIC) return EINVAL; free(cd); return 0; } /* * Get certificate Key Usage and Extended Key Usage */ /* ARGSUSED */ static krb5_error_code crypto_retieve_X509_key_usage(krb5_context context, pkinit_plg_crypto_context plgcctx, pkinit_req_crypto_context reqcctx, X509 *x, unsigned int *ret_ku_bits, unsigned int *ret_eku_bits) { /* Solaris Kerberos */ int i; unsigned int eku_bits = 0, ku_bits = 0; ASN1_BIT_STRING *usage = NULL; if (ret_ku_bits == NULL && ret_eku_bits == NULL) return EINVAL; if (ret_eku_bits) *ret_eku_bits = 0; else { pkiDebug("%s: EKUs not requested, not checking\n", __FUNCTION__); goto check_kus; } /* Start with Extended Key usage */ i = X509_get_ext_by_NID(x, NID_ext_key_usage, -1); if (i >= 0) { EXTENDED_KEY_USAGE *eku; eku = X509_get_ext_d2i(x, NID_ext_key_usage, NULL, NULL); if (eku) { for (i = 0; i < sk_ASN1_OBJECT_num(eku); i++) { ASN1_OBJECT *certoid; certoid = sk_ASN1_OBJECT_value(eku, i); if ((OBJ_cmp(certoid, plgcctx->id_pkinit_KPClientAuth)) == 0) eku_bits |= PKINIT_EKU_PKINIT; else if ((OBJ_cmp(certoid, OBJ_nid2obj(NID_ms_smartcard_login))) == 0) eku_bits |= PKINIT_EKU_MSSCLOGIN; else if ((OBJ_cmp(certoid, OBJ_nid2obj(NID_client_auth))) == 0) eku_bits |= PKINIT_EKU_CLIENTAUTH; else if ((OBJ_cmp(certoid, OBJ_nid2obj(NID_email_protect))) == 0) eku_bits |= PKINIT_EKU_EMAILPROTECTION; } EXTENDED_KEY_USAGE_free(eku); } } pkiDebug("%s: returning eku 0x%08x\n", __FUNCTION__, eku_bits); *ret_eku_bits = eku_bits; check_kus: /* Now the Key Usage bits */ if (ret_ku_bits) *ret_ku_bits = 0; else { pkiDebug("%s: KUs not requested, not checking\n", __FUNCTION__); goto out; } /* Make sure usage exists before checking bits */ usage = X509_get_ext_d2i(x, NID_key_usage, NULL, NULL); if (usage) { if (!ku_reject(x, X509v3_KU_DIGITAL_SIGNATURE)) ku_bits |= PKINIT_KU_DIGITALSIGNATURE; if (!ku_reject(x, X509v3_KU_KEY_ENCIPHERMENT)) ku_bits |= PKINIT_KU_KEYENCIPHERMENT; ASN1_BIT_STRING_free(usage); } pkiDebug("%s: returning ku 0x%08x\n", __FUNCTION__, ku_bits); *ret_ku_bits = ku_bits; out: return 0; } /* * Return a string format of an X509_NAME in buf where * size is an in/out parameter. On input it is the size * of the buffer, and on output it is the actual length * of the name. * If buf is NULL, returns the length req'd to hold name */ static char * X509_NAME_oneline_ex(X509_NAME * a, char *buf, unsigned int *size, unsigned long flag) { BIO *out = NULL; out = BIO_new(BIO_s_mem ()); if (X509_NAME_print_ex(out, a, 0, flag) > 0) { if (buf != NULL && *size > (int) BIO_number_written(out)) { (void) memset(buf, 0, *size); BIO_read(out, buf, (int) BIO_number_written(out)); } else { *size = BIO_number_written(out); } } BIO_free(out); return (buf); } /* * Get certificate information */ krb5_error_code crypto_cert_get_matching_data(krb5_context context, pkinit_cert_handle ch, pkinit_cert_matching_data **ret_md) { krb5_error_code retval; pkinit_cert_matching_data *md; krb5_principal *pkinit_sans =NULL, *upn_sans = NULL; struct _pkinit_cert_data *cd = (struct _pkinit_cert_data *)ch; int i, j; char buf[DN_BUF_LEN]; unsigned int bufsize = sizeof(buf); if (cd == NULL || cd->magic != CERT_MAGIC) return EINVAL; if (ret_md == NULL) return EINVAL; md = calloc(1, sizeof(*md)); if (md == NULL) return ENOMEM; md->ch = ch; /* get the subject name (in rfc2253 format) */ X509_NAME_oneline_ex(X509_get_subject_name(cd->cred->cert), buf, &bufsize, XN_FLAG_SEP_COMMA_PLUS); md->subject_dn = strdup(buf); if (md->subject_dn == NULL) { retval = ENOMEM; goto cleanup; } /* get the issuer name (in rfc2253 format) */ X509_NAME_oneline_ex(X509_get_issuer_name(cd->cred->cert), buf, &bufsize, XN_FLAG_SEP_COMMA_PLUS); md->issuer_dn = strdup(buf); if (md->issuer_dn == NULL) { retval = ENOMEM; goto cleanup; } /* get the san data */ retval = crypto_retrieve_X509_sans(context, cd->plgctx, cd->reqctx, cd->cred->cert, &pkinit_sans, &upn_sans, NULL); if (retval) goto cleanup; j = 0; if (pkinit_sans != NULL) { for (i = 0; pkinit_sans[i] != NULL; i++) j++; } if (upn_sans != NULL) { for (i = 0; upn_sans[i] != NULL; i++) j++; } if (j != 0) { md->sans = calloc((size_t)j+1, sizeof(*md->sans)); if (md->sans == NULL) { retval = ENOMEM; goto cleanup; } j = 0; if (pkinit_sans != NULL) { for (i = 0; pkinit_sans[i] != NULL; i++) md->sans[j++] = pkinit_sans[i]; free(pkinit_sans); } if (upn_sans != NULL) { for (i = 0; upn_sans[i] != NULL; i++) md->sans[j++] = upn_sans[i]; free(upn_sans); } md->sans[j] = NULL; } else md->sans = NULL; /* get the KU and EKU data */ retval = crypto_retieve_X509_key_usage(context, cd->plgctx, cd->reqctx, cd->cred->cert, &md->ku_bits, &md->eku_bits); if (retval) goto cleanup; *ret_md = md; retval = 0; cleanup: if (retval) { if (md) crypto_cert_free_matching_data(context, md); } return retval; } /* * Free certificate information */ krb5_error_code crypto_cert_free_matching_data(krb5_context context, pkinit_cert_matching_data *md) { krb5_principal p; int i; if (md == NULL) return EINVAL; if (md->subject_dn) free(md->subject_dn); if (md->issuer_dn) free(md->issuer_dn); if (md->sans) { for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i]) krb5_free_principal(context, p); free(md->sans); } free(md); return 0; } /* * Make this matching certificate "the chosen one" */ /* ARGSUSED */ krb5_error_code crypto_cert_select(krb5_context context, pkinit_cert_matching_data *md) { struct _pkinit_cert_data *cd; if (md == NULL) return EINVAL; cd = (struct _pkinit_cert_data *)md->ch; if (cd == NULL || cd->magic != CERT_MAGIC) return EINVAL; /* copy the selected cert into our id_cryptoctx */ if (cd->idctx->my_certs != NULL) { sk_X509_pop_free(cd->idctx->my_certs, X509_free); } cd->idctx->my_certs = sk_X509_new_null(); sk_X509_push(cd->idctx->my_certs, cd->cred->cert); cd->idctx->creds[cd->index]->cert = NULL; /* Don't free it twice */ cd->idctx->cert_index = 0; if (cd->idctx->pkcs11_method != 1) { cd->idctx->my_key = cd->cred->key; cd->idctx->creds[cd->index]->key = NULL; /* Don't free it twice */ } #ifndef WITHOUT_PKCS11 else { cd->idctx->cert_id = cd->cred->cert_id; cd->idctx->creds[cd->index]->cert_id = NULL; /* Don't free it twice */ cd->idctx->cert_id_len = cd->cred->cert_id_len; } #endif return 0; } /* * Choose the default certificate as "the chosen one" */ krb5_error_code crypto_cert_select_default(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx) { krb5_error_code retval; int cert_count = 0; retval = crypto_cert_get_count(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, &cert_count); if (retval) { pkiDebug("%s: crypto_cert_get_count error %d, %s\n", __FUNCTION__, retval, error_message(retval)); goto errout; } if (cert_count != 1) { /* Solaris Kerberos: Improved error messages */ retval = EINVAL; krb5_set_error_message(context, retval, gettext("Failed to select default certificate: " "found %d certs to choose from but there must be exactly one"), cert_count); pkiDebug("%s: ERROR: There are %d certs to choose from, " "but there must be exactly one.\n", __FUNCTION__, cert_count); goto errout; } /* copy the selected cert into our id_cryptoctx */ if (id_cryptoctx->my_certs != NULL) { sk_X509_pop_free(id_cryptoctx->my_certs, X509_free); } id_cryptoctx->my_certs = sk_X509_new_null(); sk_X509_push(id_cryptoctx->my_certs, id_cryptoctx->creds[0]->cert); id_cryptoctx->creds[0]->cert = NULL; /* Don't free it twice */ id_cryptoctx->cert_index = 0; if (id_cryptoctx->pkcs11_method != 1) { id_cryptoctx->my_key = id_cryptoctx->creds[0]->key; id_cryptoctx->creds[0]->key = NULL; /* Don't free it twice */ } #ifndef WITHOUT_PKCS11 else { id_cryptoctx->cert_id = id_cryptoctx->creds[0]->cert_id; id_cryptoctx->creds[0]->cert_id = NULL; /* Don't free it twice */ id_cryptoctx->cert_id_len = id_cryptoctx->creds[0]->cert_id_len; } #endif retval = 0; errout: return retval; } /* ARGSUSED */ static krb5_error_code load_cas_and_crls(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, int catype, char *filename) { STACK_OF(X509_INFO) *sk = NULL; STACK_OF(X509) *ca_certs = NULL; STACK_OF(X509_CRL) *ca_crls = NULL; BIO *in = NULL; /* Solaris Kerberos */ krb5_error_code retval = KRB5KRB_ERR_GENERIC; int i = 0; /* If there isn't already a stack in the context, * create a temporary one now */ switch(catype) { case CATYPE_ANCHORS: if (id_cryptoctx->trustedCAs != NULL) ca_certs = id_cryptoctx->trustedCAs; else { ca_certs = sk_X509_new_null(); if (ca_certs == NULL) return ENOMEM; } break; case CATYPE_INTERMEDIATES: if (id_cryptoctx->intermediateCAs != NULL) ca_certs = id_cryptoctx->intermediateCAs; else { ca_certs = sk_X509_new_null(); if (ca_certs == NULL) return ENOMEM; } break; case CATYPE_CRLS: if (id_cryptoctx->revoked != NULL) ca_crls = id_cryptoctx->revoked; else { ca_crls = sk_X509_CRL_new_null(); if (ca_crls == NULL) return ENOMEM; } break; default: return ENOTSUP; } if (!(in = BIO_new_file(filename, "r"))) { retval = errno; pkiDebug("%s: error opening file '%s': %s\n", __FUNCTION__, filename, error_message(errno)); goto cleanup; } /* This loads from a file, a stack of x509/crl/pkey sets */ if ((sk = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL)) == NULL) { pkiDebug("%s: error reading file '%s'\n", __FUNCTION__, filename); retval = EIO; goto cleanup; } /* scan over the stack created from loading the file contents, * weed out duplicates, and push new ones onto the return stack */ for (i = 0; i < sk_X509_INFO_num(sk); i++) { X509_INFO *xi = sk_X509_INFO_value(sk, i); if (xi != NULL && xi->x509 != NULL && catype != CATYPE_CRLS) { int j = 0, size = sk_X509_num(ca_certs), flag = 0; if (!size) { sk_X509_push(ca_certs, xi->x509); xi->x509 = NULL; continue; } for (j = 0; j < size; j++) { X509 *x = sk_X509_value(ca_certs, j); flag = X509_cmp(x, xi->x509); if (flag == 0) break; else continue; } if (flag != 0) { sk_X509_push(ca_certs, X509_dup(xi->x509)); } } else if (xi != NULL && xi->crl != NULL && catype == CATYPE_CRLS) { int j = 0, size = sk_X509_CRL_num(ca_crls), flag = 0; if (!size) { sk_X509_CRL_push(ca_crls, xi->crl); xi->crl = NULL; continue; } for (j = 0; j < size; j++) { X509_CRL *x = sk_X509_CRL_value(ca_crls, j); flag = X509_CRL_cmp(x, xi->crl); if (flag == 0) break; else continue; } if (flag != 0) { sk_X509_CRL_push(ca_crls, X509_CRL_dup(xi->crl)); } } } /* If we added something and there wasn't a stack in the * context before, add the temporary stack to the context. */ switch(catype) { case CATYPE_ANCHORS: if (sk_X509_num(ca_certs) == 0) { pkiDebug("no anchors in file, %s\n", filename); if (id_cryptoctx->trustedCAs == NULL) sk_X509_free(ca_certs); } else { if (id_cryptoctx->trustedCAs == NULL) id_cryptoctx->trustedCAs = ca_certs; } break; case CATYPE_INTERMEDIATES: if (sk_X509_num(ca_certs) == 0) { pkiDebug("no intermediates in file, %s\n", filename); if (id_cryptoctx->intermediateCAs == NULL) sk_X509_free(ca_certs); } else { if (id_cryptoctx->intermediateCAs == NULL) id_cryptoctx->intermediateCAs = ca_certs; } break; case CATYPE_CRLS: if (sk_X509_CRL_num(ca_crls) == 0) { pkiDebug("no crls in file, %s\n", filename); if (id_cryptoctx->revoked == NULL) sk_X509_CRL_free(ca_crls); } else { if (id_cryptoctx->revoked == NULL) id_cryptoctx->revoked = ca_crls; } break; default: /* Should have been caught above! */ retval = EINVAL; goto cleanup; /* Solaris Kerberos: removed "break" as it's never reached */ } retval = 0; cleanup: if (in != NULL) BIO_free(in); if (sk != NULL) sk_X509_INFO_pop_free(sk, X509_INFO_free); return retval; } static krb5_error_code load_cas_and_crls_dir(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, int catype, char *dirname) { krb5_error_code retval = EINVAL; DIR *d = NULL; struct dirent *dentry = NULL; char filename[1024]; if (dirname == NULL) return EINVAL; d = opendir(dirname); if (d == NULL) return ENOENT; while ((dentry = readdir(d))) { if (strlen(dirname) + strlen(dentry->d_name) + 2 > sizeof(filename)) { pkiDebug("%s: Path too long -- directory '%s' and file '%s'\n", __FUNCTION__, dirname, dentry->d_name); goto cleanup; } /* Ignore subdirectories and anything starting with a dot */ #ifdef DT_DIR if (dentry->d_type == DT_DIR) continue; #endif if (dentry->d_name[0] == '.') continue; (void) snprintf(filename, sizeof(filename), "%s/%s", dirname, dentry->d_name); retval = load_cas_and_crls(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, catype, filename); if (retval) goto cleanup; } retval = 0; cleanup: if (d != NULL) (void) closedir(d); return retval; } /* ARGSUSED */ krb5_error_code crypto_load_cas_and_crls(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_opts *idopts, pkinit_identity_crypto_context id_cryptoctx, int idtype, int catype, char *id) { pkiDebug("%s: called with idtype %s and catype %s\n", __FUNCTION__, idtype2string(idtype), catype2string(catype)); /* Solaris Kerberos: Removed "break"'s as they are never reached */ switch (idtype) { case IDTYPE_FILE: return load_cas_and_crls(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, catype, id); case IDTYPE_DIR: return load_cas_and_crls_dir(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, catype, id); default: return ENOTSUP; } } static krb5_error_code create_identifiers_from_stack(STACK_OF(X509) *sk, krb5_external_principal_identifier *** ids) { krb5_error_code retval = ENOMEM; int i = 0, sk_size = sk_X509_num(sk); krb5_external_principal_identifier **krb5_cas = NULL; X509 *x = NULL; X509_NAME *xn = NULL; unsigned char *p = NULL; int len = 0; PKCS7_ISSUER_AND_SERIAL *is = NULL; char buf[DN_BUF_LEN]; *ids = NULL; krb5_cas = malloc((sk_size + 1) * sizeof(krb5_external_principal_identifier *)); if (krb5_cas == NULL) return ENOMEM; krb5_cas[sk_size] = NULL; for (i = 0; i < sk_size; i++) { krb5_cas[i] = (krb5_external_principal_identifier *)malloc(sizeof(krb5_external_principal_identifier)); x = sk_X509_value(sk, i); X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf)); pkiDebug("#%d cert= %s\n", i, buf); /* fill-in subjectName */ krb5_cas[i]->subjectName.magic = 0; krb5_cas[i]->subjectName.length = 0; krb5_cas[i]->subjectName.data = NULL; xn = X509_get_subject_name(x); len = i2d_X509_NAME(xn, NULL); if ((p = krb5_cas[i]->subjectName.data = (unsigned char *)malloc((size_t) len)) == NULL) goto cleanup; i2d_X509_NAME(xn, &p); krb5_cas[i]->subjectName.length = len; /* fill-in issuerAndSerialNumber */ krb5_cas[i]->issuerAndSerialNumber.length = 0; krb5_cas[i]->issuerAndSerialNumber.magic = 0; krb5_cas[i]->issuerAndSerialNumber.data = NULL; #ifdef LONGHORN_BETA_COMPAT if (longhorn == 0) { /* XXX Longhorn doesn't like this */ #endif is = PKCS7_ISSUER_AND_SERIAL_new(); X509_NAME_set(&is->issuer, X509_get_issuer_name(x)); ASN1_INTEGER_free(is->serial); is->serial = ASN1_INTEGER_dup(X509_get_serialNumber(x)); len = i2d_PKCS7_ISSUER_AND_SERIAL(is, NULL); if ((p = krb5_cas[i]->issuerAndSerialNumber.data = (unsigned char *)malloc((size_t) len)) == NULL) goto cleanup; i2d_PKCS7_ISSUER_AND_SERIAL(is, &p); krb5_cas[i]->issuerAndSerialNumber.length = len; #ifdef LONGHORN_BETA_COMPAT } #endif /* fill-in subjectKeyIdentifier */ krb5_cas[i]->subjectKeyIdentifier.length = 0; krb5_cas[i]->subjectKeyIdentifier.magic = 0; krb5_cas[i]->subjectKeyIdentifier.data = NULL; #ifdef LONGHORN_BETA_COMPAT if (longhorn == 0) { /* XXX Longhorn doesn't like this */ #endif if (X509_get_ext_by_NID(x, NID_subject_key_identifier, -1) >= 0) { ASN1_OCTET_STRING *ikeyid = NULL; if ((ikeyid = X509_get_ext_d2i(x, NID_subject_key_identifier, NULL, NULL))) { len = i2d_ASN1_OCTET_STRING(ikeyid, NULL); if ((p = krb5_cas[i]->subjectKeyIdentifier.data = (unsigned char *)malloc((size_t) len)) == NULL) goto cleanup; i2d_ASN1_OCTET_STRING(ikeyid, &p); krb5_cas[i]->subjectKeyIdentifier.length = len; } if (ikeyid != NULL) ASN1_OCTET_STRING_free(ikeyid); } #ifdef LONGHORN_BETA_COMPAT } #endif if (is != NULL) { if (is->issuer != NULL) X509_NAME_free(is->issuer); if (is->serial != NULL) ASN1_INTEGER_free(is->serial); free(is); } } *ids = krb5_cas; retval = 0; cleanup: if (retval) free_krb5_external_principal_identifier(&krb5_cas); return retval; } /* ARGSUSED */ static krb5_error_code create_krb5_invalidCertificates(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_external_principal_identifier *** ids) { krb5_error_code retval = ENOMEM; STACK_OF(X509) *sk = NULL; *ids = NULL; if (req_cryptoctx->received_cert == NULL) return KRB5KDC_ERR_PREAUTH_FAILED; sk = sk_X509_new_null(); if (sk == NULL) goto cleanup; sk_X509_push(sk, req_cryptoctx->received_cert); retval = create_identifiers_from_stack(sk, ids); sk_X509_free(sk); cleanup: return retval; } /* ARGSUSED */ krb5_error_code create_krb5_supportedCMSTypes(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_algorithm_identifier ***oids) { krb5_error_code retval = ENOMEM; krb5_algorithm_identifier **loids = NULL; krb5_octet_data des3oid = {0, 8, (unsigned char *)"\x2A\x86\x48\x86\xF7\x0D\x03\x07" }; *oids = NULL; loids = malloc(2 * sizeof(krb5_algorithm_identifier *)); if (loids == NULL) goto cleanup; loids[1] = NULL; loids[0] = (krb5_algorithm_identifier *)malloc(sizeof(krb5_algorithm_identifier)); if (loids[0] == NULL) { free(loids); goto cleanup; } retval = pkinit_copy_krb5_octet_data(&loids[0]->algorithm, &des3oid); if (retval) { free(loids[0]); free(loids); goto cleanup; } loids[0]->parameters.length = 0; loids[0]->parameters.data = NULL; *oids = loids; retval = 0; cleanup: return retval; } /* ARGSUSED */ krb5_error_code create_krb5_trustedCertifiers(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_external_principal_identifier *** ids) { /* Solaris Kerberos */ STACK_OF(X509) *sk = id_cryptoctx->trustedCAs; *ids = NULL; if (id_cryptoctx->trustedCAs == NULL) return KRB5KDC_ERR_PREAUTH_FAILED; return create_identifiers_from_stack(sk, ids); } /* ARGSUSED */ krb5_error_code create_krb5_trustedCas(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, int flag, krb5_trusted_ca *** ids) { krb5_error_code retval = ENOMEM; STACK_OF(X509) *sk = id_cryptoctx->trustedCAs; int i = 0, len = 0, sk_size = sk_X509_num(sk); krb5_trusted_ca **krb5_cas = NULL; X509 *x = NULL; char buf[DN_BUF_LEN]; X509_NAME *xn = NULL; unsigned char *p = NULL; PKCS7_ISSUER_AND_SERIAL *is = NULL; *ids = NULL; if (id_cryptoctx->trustedCAs == NULL) return KRB5KDC_ERR_PREAUTH_FAILED; krb5_cas = malloc((sk_size + 1) * sizeof(krb5_trusted_ca *)); if (krb5_cas == NULL) return ENOMEM; krb5_cas[sk_size] = NULL; for (i = 0; i < sk_size; i++) { krb5_cas[i] = (krb5_trusted_ca *)malloc(sizeof(krb5_trusted_ca)); if (krb5_cas[i] == NULL) goto cleanup; x = sk_X509_value(sk, i); X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf)); pkiDebug("#%d cert= %s\n", i, buf); switch (flag) { case choice_trusted_cas_principalName: krb5_cas[i]->choice = choice_trusted_cas_principalName; break; case choice_trusted_cas_caName: krb5_cas[i]->choice = choice_trusted_cas_caName; krb5_cas[i]->u.caName.data = NULL; krb5_cas[i]->u.caName.length = 0; xn = X509_get_subject_name(x); len = i2d_X509_NAME(xn, NULL); if ((p = krb5_cas[i]->u.caName.data = (unsigned char *)malloc((size_t) len)) == NULL) goto cleanup; i2d_X509_NAME(xn, &p); krb5_cas[i]->u.caName.length = len; break; case choice_trusted_cas_issuerAndSerial: krb5_cas[i]->choice = choice_trusted_cas_issuerAndSerial; krb5_cas[i]->u.issuerAndSerial.data = NULL; krb5_cas[i]->u.issuerAndSerial.length = 0; is = PKCS7_ISSUER_AND_SERIAL_new(); X509_NAME_set(&is->issuer, X509_get_issuer_name(x)); ASN1_INTEGER_free(is->serial); is->serial = ASN1_INTEGER_dup(X509_get_serialNumber(x)); len = i2d_PKCS7_ISSUER_AND_SERIAL(is, NULL); if ((p = krb5_cas[i]->u.issuerAndSerial.data = (unsigned char *)malloc((size_t) len)) == NULL) goto cleanup; i2d_PKCS7_ISSUER_AND_SERIAL(is, &p); krb5_cas[i]->u.issuerAndSerial.length = len; if (is != NULL) { if (is->issuer != NULL) X509_NAME_free(is->issuer); if (is->serial != NULL) ASN1_INTEGER_free(is->serial); free(is); } break; default: break; } } retval = 0; *ids = krb5_cas; cleanup: if (retval) free_krb5_trusted_ca(&krb5_cas); return retval; } /* ARGSUSED */ krb5_error_code create_issuerAndSerial(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, unsigned char **out, unsigned int *out_len) { unsigned char *p = NULL; PKCS7_ISSUER_AND_SERIAL *is = NULL; int len = 0; krb5_error_code retval = ENOMEM; X509 *cert = req_cryptoctx->received_cert; *out = NULL; *out_len = 0; if (req_cryptoctx->received_cert == NULL) return 0; is = PKCS7_ISSUER_AND_SERIAL_new(); X509_NAME_set(&is->issuer, X509_get_issuer_name(cert)); ASN1_INTEGER_free(is->serial); is->serial = ASN1_INTEGER_dup(X509_get_serialNumber(cert)); len = i2d_PKCS7_ISSUER_AND_SERIAL(is, NULL); if ((p = *out = (unsigned char *)malloc((size_t) len)) == NULL) goto cleanup; i2d_PKCS7_ISSUER_AND_SERIAL(is, &p); *out_len = len; retval = 0; cleanup: X509_NAME_free(is->issuer); ASN1_INTEGER_free(is->serial); free(is); return retval; } static int pkcs7_decrypt(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, PKCS7 *p7, BIO *data) { BIO *tmpmem = NULL; /* Solaris Kerberos */ int i = 0; char buf[4096]; if(p7 == NULL) return 0; if(!PKCS7_type_is_enveloped(p7)) { pkiDebug("wrong pkcs7 content type\n"); return 0; } if(!(tmpmem = pkcs7_dataDecode(context, id_cryptoctx, p7))) { pkiDebug("unable to decrypt pkcs7 object\n"); return 0; } for(;;) { i = BIO_read(tmpmem, buf, sizeof(buf)); if (i <= 0) break; BIO_write(data, buf, i); BIO_free_all(tmpmem); return 1; } return 0; } krb5_error_code pkinit_process_td_trusted_certifiers( krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_external_principal_identifier **krb5_trusted_certifiers, int td_type) { krb5_error_code retval = ENOMEM; STACK_OF(X509_NAME) *sk_xn = NULL; X509_NAME *xn = NULL; PKCS7_ISSUER_AND_SERIAL *is = NULL; ASN1_OCTET_STRING *id = NULL; const unsigned char *p = NULL; char buf[DN_BUF_LEN]; int i = 0; if (td_type == TD_TRUSTED_CERTIFIERS) pkiDebug("received trusted certifiers\n"); else pkiDebug("received invalid certificate\n"); sk_xn = sk_X509_NAME_new_null(); while(krb5_trusted_certifiers[i] != NULL) { if (krb5_trusted_certifiers[i]->subjectName.data != NULL) { p = krb5_trusted_certifiers[i]->subjectName.data; xn = d2i_X509_NAME(NULL, &p, (int)krb5_trusted_certifiers[i]->subjectName.length); if (xn == NULL) goto cleanup; X509_NAME_oneline(xn, buf, sizeof(buf)); if (td_type == TD_TRUSTED_CERTIFIERS) pkiDebug("#%d cert = %s is trusted by kdc\n", i, buf); else pkiDebug("#%d cert = %s is invalid\n", i, buf); sk_X509_NAME_push(sk_xn, xn); } if (krb5_trusted_certifiers[i]->issuerAndSerialNumber.data != NULL) { p = krb5_trusted_certifiers[i]->issuerAndSerialNumber.data; is = d2i_PKCS7_ISSUER_AND_SERIAL(NULL, &p, (int)krb5_trusted_certifiers[i]->issuerAndSerialNumber.length); if (is == NULL) goto cleanup; X509_NAME_oneline(is->issuer, buf, sizeof(buf)); if (td_type == TD_TRUSTED_CERTIFIERS) pkiDebug("#%d issuer = %s serial = %ld is trusted bu kdc\n", i, buf, ASN1_INTEGER_get(is->serial)); else pkiDebug("#%d issuer = %s serial = %ld is invalid\n", i, buf, ASN1_INTEGER_get(is->serial)); PKCS7_ISSUER_AND_SERIAL_free(is); } if (krb5_trusted_certifiers[i]->subjectKeyIdentifier.data != NULL) { p = krb5_trusted_certifiers[i]->subjectKeyIdentifier.data; id = d2i_ASN1_OCTET_STRING(NULL, &p, (int)krb5_trusted_certifiers[i]->subjectKeyIdentifier.length); if (id == NULL) goto cleanup; /* XXX */ ASN1_OCTET_STRING_free(id); } i++; } /* XXX Since we not doing anything with received trusted certifiers * return an error. this is the place where we can pick a different * client certificate based on the information in td_trusted_certifiers */ retval = KRB5KDC_ERR_PREAUTH_FAILED; cleanup: if (sk_xn != NULL) sk_X509_NAME_pop_free(sk_xn, X509_NAME_free); return retval; } /* * EVP_CIPHER_asn1_to_param is not exposed in LibreSSL's public headers * but is available in libcrypto. This function is used below in our * local reimplementation of PKCS7_dataDecode to set cipher parameters * from the ASN.1 AlgorithmIdentifier. */ extern int EVP_CIPHER_asn1_to_param(EVP_CIPHER_CTX *ctx, ASN1_TYPE *type); /* * PKCS7error() is LibreSSL's internal error reporting macro (defined in * crypto/err/err_local.h, not public). Define it here using the public API. */ #ifndef PKCS7error #define PKCS7error(r) \ ERR_PUT_error(ERR_LIB_PKCS7, 0, (r), __FILE__, __LINE__) #endif static BIO * pkcs7_dataDecode(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, PKCS7 *p7) { int i = 0; unsigned int jj = 0, tmp_len = 0; BIO *out=NULL,*etmp=NULL,*bio=NULL; unsigned char *tmp=NULL; ASN1_OCTET_STRING *data_body=NULL; const EVP_CIPHER *evp_cipher=NULL; EVP_CIPHER_CTX *evp_ctx=NULL; X509_ALGOR *enc_alg=NULL; STACK_OF(PKCS7_RECIP_INFO) *rsk=NULL; /* Solaris Kerberos: Not used */ #if 0 X509_ALGOR *xalg=NULL; #endif PKCS7_RECIP_INFO *ri=NULL; X509 *cert = sk_X509_value(id_cryptoctx->my_certs, id_cryptoctx->cert_index); p7->state=PKCS7_S_HEADER; rsk=p7->d.enveloped->recipientinfo; enc_alg=p7->d.enveloped->enc_data->algorithm; data_body=p7->d.enveloped->enc_data->enc_data; evp_cipher=EVP_get_cipherbyobj(enc_alg->algorithm); if (evp_cipher == NULL) { PKCS7error(PKCS7_R_UNSUPPORTED_CIPHER_TYPE); goto cleanup; } /* Solaris Kerberos: Not used */ #if 0 xalg=p7->d.enveloped->enc_data->algorithm; #endif if ((etmp=BIO_new(BIO_f_cipher())) == NULL) { PKCS7error(ERR_R_BIO_LIB); goto cleanup; } /* It was encrypted, we need to decrypt the secret key * with the private key */ /* Find the recipientInfo which matches the passed certificate * (if any) */ if (cert) { for (i=0; iissuer_and_serial->issuer, X509_get_issuer_name(cert)); if (!tmp_ret) { tmp_ret = ASN1_INTEGER_cmp(X509_get_serialNumber(cert), ri->issuer_and_serial->serial); if (!tmp_ret) break; } ri=NULL; } if (ri == NULL) { PKCS7error(PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE); goto cleanup; } } /* If we haven't got a certificate try each ri in turn */ if (cert == NULL) { for (i=0; ienc_key), ASN1_STRING_length(ri->enc_key), &tmp, &tmp_len); if (jj) { PKCS7error(ERR_R_EVP_LIB); goto cleanup; } if (!jj && tmp_len > 0) { jj = tmp_len; break; } ERR_clear_error(); ri = NULL; } if (ri == NULL) { PKCS7error(PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE); goto cleanup; } } else { jj = pkinit_decode_data(context, id_cryptoctx, (unsigned char *)ASN1_STRING_get0_data(ri->enc_key), ASN1_STRING_length(ri->enc_key), &tmp, &tmp_len); /* Solaris Kerberos: tmp_len is unsigned. Cannot be < 0 */ if (jj || tmp_len == 0) { PKCS7error(ERR_R_EVP_LIB); goto cleanup; } jj = tmp_len; } evp_ctx=NULL; BIO_get_cipher_ctx(etmp,&evp_ctx); if (EVP_CipherInit_ex(evp_ctx,evp_cipher,NULL,NULL,NULL,0) <= 0) goto cleanup; if (EVP_CIPHER_asn1_to_param(evp_ctx,enc_alg->parameter) < 0) goto cleanup; if (jj != EVP_CIPHER_CTX_key_length(evp_ctx)) { /* Some S/MIME clients don't use the same key * and effective key length. The key length is * determined by the size of the decrypted RSA key. */ if(!EVP_CIPHER_CTX_set_key_length(evp_ctx, (int)jj)) { PKCS7error(PKCS7_R_DECRYPT_ERROR); goto cleanup; } } if (EVP_CipherInit_ex(evp_ctx,NULL,NULL,tmp,NULL,0) <= 0) goto cleanup; OPENSSL_cleanse(tmp,jj); if (out == NULL) out=etmp; else BIO_push(out,etmp); etmp=NULL; if (data_body->length > 0) bio = BIO_new_mem_buf(data_body->data, data_body->length); else { bio=BIO_new(BIO_s_mem()); BIO_set_mem_eof_return(bio,0); } BIO_push(out,bio); bio=NULL; /* Solaris Kerberos */ goto out; cleanup: if (out != NULL) BIO_free_all(out); if (etmp != NULL) BIO_free_all(etmp); if (bio != NULL) BIO_free_all(bio); out=NULL; out: if (tmp != NULL) free(tmp); return(out); } static krb5_error_code der_decode_data(unsigned char *data, long data_len, unsigned char **out, long *out_len) { /* Solaris Kerberos */ krb5_error_code retval = KRB5KRB_ERR_GENERIC; ASN1_OCTET_STRING *s = NULL; const unsigned char *p = data; if ((s = d2i_ASN1_BIT_STRING(NULL, &p, data_len)) == NULL) goto cleanup; *out_len = s->length; if ((*out = (unsigned char *) malloc((size_t) *out_len + 1)) == NULL) { retval = ENOMEM; goto cleanup; } (void) memcpy(*out, s->data, (size_t) s->length); (*out)[s->length] = '\0'; retval = 0; cleanup: if (s != NULL) ASN1_OCTET_STRING_free(s); return retval; } #ifdef DEBUG_DH static void print_dh(DH * dh, char *msg) { BIO *bio_err = NULL; bio_err = BIO_new(BIO_s_file()); BIO_set_fp(bio_err, stderr, BIO_NOCLOSE | BIO_FP_TEXT); if (msg) BIO_puts(bio_err, (const char *)msg); if (dh) DHparams_print(bio_err, dh); BN_print(bio_err, DH_get0_q(dh)); BIO_puts(bio_err, (const char *)"\n"); BIO_free(bio_err); } static void print_pubkey(BIGNUM * key, char *msg) { BIO *bio_err = NULL; bio_err = BIO_new(BIO_s_file()); BIO_set_fp(bio_err, stderr, BIO_NOCLOSE | BIO_FP_TEXT); if (msg) BIO_puts(bio_err, (const char *)msg); if (key) BN_print(bio_err, key); BIO_puts(bio_err, "\n"); BIO_free(bio_err); } #endif /* * Solaris Kerberos: * Error message generation has changed so gettext() can be used */ #if 0 static char * pkinit_pkcs11_code_to_text(int err) { int i; static char uc[64]; for (i = 0; pkcs11_errstrings[i].text != NULL; i++) if (pkcs11_errstrings[i].code == err) break; if (pkcs11_errstrings[i].text != NULL) return (pkcs11_errstrings[i].text); snprintf(uc, 64, gettext("unknown code 0x%x"), err); return (uc); } #endif static char * pkinit_pkcs11_code_to_text(int err) { return pkcs11_error_table(err); } /* * COPYRIGHT (C) 2006,2007 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN * ALL RIGHTS RESERVED * * Permission is granted to use, copy, create derivative works * and redistribute this software and such derivative works * for any purpose, so long as the name of The University of * Michigan is not used in any advertising or publicity * pertaining to the use of distribution of this software * without specific, written prior authorization. If the * above copyright notice or any other identification of the * University of Michigan is included in any copy of any * portion of this software, then the disclaimer below must * also be included. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF * SUCH DAMAGES. */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. * Copyright 2018 RackTop Systems. */ #ifndef _PKINIT_CRYPTO_OPENSSL_H #define _PKINIT_CRYPTO_OPENSSL_H #include #include #include #include #include #include #include #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER < 0x10100000L #include #else #include #endif #include "pkinit.h" #define DN_BUF_LEN 256 #define MAX_CREDS_ALLOWED 20 struct _pkinit_cred_info { X509 *cert; EVP_PKEY *key; #ifndef WITHOUT_PKCS11 CK_BYTE_PTR cert_id; int cert_id_len; #endif }; typedef struct _pkinit_cred_info * pkinit_cred_info; struct _pkinit_identity_crypto_context { pkinit_cred_info creds[MAX_CREDS_ALLOWED+1]; STACK_OF(X509) *my_certs; /* available user certs */ int cert_index; /* cert to use out of available certs*/ EVP_PKEY *my_key; /* available user keys if in filesystem */ STACK_OF(X509) *trustedCAs; /* available trusted ca certs */ STACK_OF(X509) *intermediateCAs; /* available intermediate ca certs */ STACK_OF(X509_CRL) *revoked; /* available crls */ int pkcs11_method; krb5_prompter_fct prompter; void *prompter_data; #ifndef WITHOUT_PKCS11 char *p11_module_name; CK_SLOT_ID slotid; char *token_label; char *cert_label; char *PIN; /* Solaris Kerberos: */ /* These are crypto-specific */ void *p11_module; CK_SESSION_HANDLE session; CK_FUNCTION_LIST_PTR p11; CK_BYTE_PTR cert_id; int cert_id_len; CK_MECHANISM_TYPE mech; /* Solaris Kerberos: need to keep some state */ uint_t p11flags; /* * Solaris Kerberos: * If PKCS#11 is already being used by the process then C_Finalize should * not be called by pkinit as it would invalidate any PKCS#11 sessions the * process was using prior to loading the pkinit plugin. "finalize_pkcs11" * indicates whether or not C_Finalize should be called by pkinit. */ krb5_boolean finalize_pkcs11; #endif }; /* Solaris Kerberos: need to know if login was done */ #define C_LOGIN_DONE 0x1 /* The session is logged in. */ #define C_PROMPTED_USER 0x2 /* The user was prompted for token. */ #define C_SKIP_PKCS11_AUTH 0x4 /* User does not want to do PKCS11 auth */ struct _pkinit_plg_crypto_context { DH *dh_1024; DH *dh_2048; DH *dh_4096; ASN1_OBJECT *id_pkinit_authData; ASN1_OBJECT *id_pkinit_DHKeyData; ASN1_OBJECT *id_pkinit_rkeyData; ASN1_OBJECT *id_pkinit_san; ASN1_OBJECT *id_ms_san_upn; ASN1_OBJECT *id_pkinit_KPClientAuth; ASN1_OBJECT *id_pkinit_KPKdc; ASN1_OBJECT *id_ms_kp_sc_logon; ASN1_OBJECT *id_kp_serverAuth; }; struct _pkinit_req_crypto_context { X509 *received_cert; DH *dh; }; #define CERT_MAGIC 0x53534c43 struct _pkinit_cert_data { unsigned int magic; pkinit_plg_crypto_context plgctx; pkinit_req_crypto_context reqctx; pkinit_identity_crypto_context idctx; pkinit_cred_info cred; unsigned int index; /* Index of this cred in the creds[] array */ }; #define ITER_MAGIC 0x53534c49 struct _pkinit_cert_iter_data { unsigned int magic; pkinit_plg_crypto_context plgctx; pkinit_req_crypto_context reqctx; pkinit_identity_crypto_context idctx; unsigned int index; }; /* Solaris Kerberos */ static krb5_error_code openssl_init(void); static krb5_error_code pkinit_init_pkinit_oids(pkinit_plg_crypto_context ); static void pkinit_fini_pkinit_oids(pkinit_plg_crypto_context ); static krb5_error_code pkinit_init_dh_params(pkinit_plg_crypto_context ); static void pkinit_fini_dh_params(pkinit_plg_crypto_context ); static krb5_error_code pkinit_init_certs(pkinit_identity_crypto_context ctx); static void pkinit_fini_certs(pkinit_identity_crypto_context ctx); static krb5_error_code pkinit_init_pkcs11(pkinit_identity_crypto_context ctx); static void pkinit_fini_pkcs11(pkinit_identity_crypto_context ctx); static krb5_error_code pkinit_encode_dh_params (const BIGNUM *, const BIGNUM *, const BIGNUM *, unsigned char **, unsigned int *); static DH *pkinit_decode_dh_params (DH **, unsigned char **, unsigned int ); static int pkinit_check_dh_params (const BIGNUM *p1, const BIGNUM *p2, const BIGNUM *g1, const BIGNUM *q1); static krb5_error_code pkinit_sign_data (krb5_context context, pkinit_identity_crypto_context cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **sig, unsigned int *sig_len); static krb5_error_code create_signature (unsigned char **, unsigned int *, unsigned char *, unsigned int, EVP_PKEY *pkey); static krb5_error_code pkinit_decode_data (krb5_context context, pkinit_identity_crypto_context cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **decoded, unsigned int *decoded_len); static krb5_error_code decode_data (unsigned char **, unsigned int *, unsigned char *, unsigned int, EVP_PKEY *pkey, X509 *cert); #ifdef DEBUG_DH static void print_dh(DH *, char *); static void print_pubkey(BIGNUM *, char *); #endif static int prepare_enc_data (unsigned char *indata, int indata_len, unsigned char **outdata, int *outdata_len); static int openssl_callback (int, X509_STORE_CTX *); static int openssl_callback_ignore_crls (int, X509_STORE_CTX *); static int pkcs7_decrypt (krb5_context context, pkinit_identity_crypto_context id_cryptoctx, PKCS7 *p7, BIO *bio); static BIO * pkcs7_dataDecode (krb5_context context, pkinit_identity_crypto_context id_cryptoctx, PKCS7 *p7); static ASN1_OBJECT * pkinit_pkcs7type2oid (pkinit_plg_crypto_context plg_cryptoctx, int pkcs7_type); static krb5_error_code pkinit_create_sequence_of_principal_identifiers (krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, int type, krb5_data **out_data); #ifndef WITHOUT_PKCS11 static krb5_error_code pkinit_find_private_key (pkinit_identity_crypto_context, CK_ATTRIBUTE_TYPE usage, CK_OBJECT_HANDLE *objp); static krb5_error_code pkinit_login (krb5_context context, pkinit_identity_crypto_context id_cryptoctx, CK_TOKEN_INFO *tip); static void * pkinit_C_LoadModule(const char *modname, CK_FUNCTION_LIST_PTR_PTR p11p); static CK_RV pkinit_C_UnloadModule(void *handle); #ifdef SILLYDECRYPT CK_RV pkinit_C_Decrypt (pkinit_identity_crypto_context id_cryptoctx, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen); #endif static krb5_error_code pkinit_sign_data_pkcs11 (krb5_context context, pkinit_identity_crypto_context id_cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **sig, unsigned int *sig_len); static krb5_error_code pkinit_decode_data_pkcs11 (krb5_context context, pkinit_identity_crypto_context id_cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **decoded_data, unsigned int *decoded_data_len); #endif /* WITHOUT_PKCS11 */ static krb5_error_code pkinit_sign_data_fs (krb5_context context, pkinit_identity_crypto_context id_cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **sig, unsigned int *sig_len); static krb5_error_code pkinit_decode_data_fs (krb5_context context, pkinit_identity_crypto_context id_cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **decoded_data, unsigned int *decoded_data_len); static krb5_error_code der_decode_data (unsigned char *, long, unsigned char **, long *); static krb5_error_code create_krb5_invalidCertificates(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_external_principal_identifier *** ids); static krb5_error_code create_identifiers_from_stack(STACK_OF(X509) *sk, krb5_external_principal_identifier *** ids); #ifdef LONGHORN_BETA_COMPAT static int wrap_signeddata(unsigned char *data, unsigned int data_len, unsigned char **out, unsigned int *out_len, int is_longhorn_server); #else static int wrap_signeddata(unsigned char *data, unsigned int data_len, unsigned char **out, unsigned int *out_len); #endif /* This handy macro borrowed from crypto/x509v3/v3_purp.c */ #if OPENSSL_VERSION_NUMBER < 0x10100000L #define ku_reject(x, usage) \ (((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage))) #else #define ku_reject(x, usage) \ ((X509_get_extension_flags(x) & EXFLAG_KUSAGE) && \ !(X509_get_key_usage(x) & (usage))) #endif static char * pkinit_pkcs11_code_to_text(int err); #endif /* _PKINIT_CRYPTO_OPENSSL_H */ /* * COPYRIGHT (C) 2007 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN * ALL RIGHTS RESERVED * * Permission is granted to use, copy, create derivative works * and redistribute this software and such derivative works * for any purpose, so long as the name of The University of * Michigan is not used in any advertising or publicity * pertaining to the use of distribution of this software * without specific, written prior authorization. If the * above copyright notice or any other identification of the * University of Michigan is included in any copy of any * portion of this software, then the disclaimer below must * also be included. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF * SUCH DAMAGES. */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include "pkinit.h" static void free_list(char **list) { int i; if (list == NULL) return; for (i = 0; list[i] != NULL; i++) free(list[i]); free(list); } static krb5_error_code copy_list(char ***dst, char **src) { int i; char **newlist; if (dst == NULL) return EINVAL; *dst = NULL; if (src == NULL) return 0; for (i = 0; src[i] != NULL; i++); newlist = calloc(1, (i + 1) * sizeof(*newlist)); if (newlist == NULL) return ENOMEM; for (i = 0; src[i] != NULL; i++) { newlist[i] = strdup(src[i]); if (newlist[i] == NULL) goto cleanup; } newlist[i] = NULL; *dst = newlist; return 0; cleanup: free_list(newlist); return ENOMEM; } char * idtype2string(int idtype) { /* Solaris Kerberos: Removed "break"s (lint) */ switch(idtype) { case IDTYPE_FILE: return "FILE"; case IDTYPE_DIR: return "DIR"; case IDTYPE_PKCS11: return "PKCS11"; case IDTYPE_PKCS12: return "PKCS12"; case IDTYPE_ENVVAR: return "ENV"; default: return "INVALID"; } } char * catype2string(int catype) { /* Solaris Kerberos: Removed "break"s (lint) */ switch(catype) { case CATYPE_ANCHORS: return "ANCHORS"; case CATYPE_INTERMEDIATES: return "INTERMEDIATES"; case CATYPE_CRLS: return "CRLS"; default: return "INVALID"; } } krb5_error_code pkinit_init_identity_opts(pkinit_identity_opts **idopts) { pkinit_identity_opts *opts = NULL; *idopts = NULL; opts = (pkinit_identity_opts *) calloc(1, sizeof(pkinit_identity_opts)); if (opts == NULL) return ENOMEM; opts->identity = NULL; opts->anchors = NULL; opts->intermediates = NULL; opts->crls = NULL; opts->ocsp = NULL; opts->dn_mapping_file = NULL; opts->cert_filename = NULL; opts->key_filename = NULL; #ifndef WITHOUT_PKCS11 opts->p11_module_name = NULL; opts->slotid = PK_NOSLOT; opts->token_label = NULL; opts->cert_id_string = NULL; opts->cert_label = NULL; opts->PIN = NULL; #endif *idopts = opts; return 0; } krb5_error_code pkinit_dup_identity_opts(pkinit_identity_opts *src_opts, pkinit_identity_opts **dest_opts) { pkinit_identity_opts *newopts; krb5_error_code retval; *dest_opts = NULL; retval = pkinit_init_identity_opts(&newopts); if (retval) return retval; retval = ENOMEM; if (src_opts->identity != NULL) { newopts->identity = strdup(src_opts->identity); if (newopts->identity == NULL) goto cleanup; } retval = copy_list(&newopts->anchors, src_opts->anchors); if (retval) goto cleanup; retval = copy_list(&newopts->intermediates,src_opts->intermediates); if (retval) goto cleanup; retval = copy_list(&newopts->crls, src_opts->crls); if (retval) goto cleanup; if (src_opts->ocsp != NULL) { newopts->ocsp = strdup(src_opts->ocsp); if (newopts->ocsp == NULL) goto cleanup; } if (src_opts->cert_filename != NULL) { newopts->cert_filename = strdup(src_opts->cert_filename); if (newopts->cert_filename == NULL) goto cleanup; } if (src_opts->key_filename != NULL) { newopts->key_filename = strdup(src_opts->key_filename); if (newopts->key_filename == NULL) goto cleanup; } #ifndef WITHOUT_PKCS11 if (src_opts->p11_module_name != NULL) { newopts->p11_module_name = strdup(src_opts->p11_module_name); if (newopts->p11_module_name == NULL) goto cleanup; } newopts->slotid = src_opts->slotid; if (src_opts->token_label != NULL) { newopts->token_label = strdup(src_opts->token_label); if (newopts->token_label == NULL) goto cleanup; } if (src_opts->cert_id_string != NULL) { newopts->cert_id_string = strdup(src_opts->cert_id_string); if (newopts->cert_id_string == NULL) goto cleanup; } if (src_opts->cert_label != NULL) { newopts->cert_label = strdup(src_opts->cert_label); if (newopts->cert_label == NULL) goto cleanup; } if (src_opts->PIN != NULL) { newopts->PIN = strdup(src_opts->PIN); if (newopts->PIN == NULL) goto cleanup; } #endif *dest_opts = newopts; return 0; cleanup: pkinit_fini_identity_opts(newopts); return retval; } void pkinit_fini_identity_opts(pkinit_identity_opts *idopts) { if (idopts == NULL) return; if (idopts->identity != NULL) free(idopts->identity); free_list(idopts->anchors); free_list(idopts->intermediates); free_list(idopts->crls); free_list(idopts->identity_alt); if (idopts->cert_filename != NULL) free(idopts->cert_filename); if (idopts->key_filename != NULL) free(idopts->key_filename); #ifndef WITHOUT_PKCS11 if (idopts->p11_module_name != NULL) free(idopts->p11_module_name); if (idopts->token_label != NULL) free(idopts->token_label); if (idopts->cert_id_string != NULL) free(idopts->cert_id_string); if (idopts->cert_label != NULL) free(idopts->cert_label); if (idopts->PIN != NULL) { (void) memset(idopts->PIN, 0, strlen(idopts->PIN)); free(idopts->PIN); } #endif free(idopts); } #ifndef WITHOUT_PKCS11 /* ARGSUSED */ static krb5_error_code parse_pkcs11_options(krb5_context context, pkinit_identity_opts *idopts, const char *residual) { char *s, *cp, *vp; krb5_error_code retval = ENOMEM; if (residual == NULL || residual[0] == '\0') return 0; /* Split string into attr=value substrings */ s = strdup(residual); if (s == NULL) return retval; for ((cp = strtok(s, ":")); cp; (cp = strtok(NULL, ":"))) { vp = strchr(cp, '='); /* If there is no "=", this is a pkcs11 module name */ if (vp == NULL) { if (idopts->p11_module_name != NULL) free(idopts->p11_module_name); idopts->p11_module_name = strdup(cp); if (idopts->p11_module_name == NULL) goto cleanup; continue; } *vp++ = '\0'; if (!strcmp(cp, "module_name")) { if (idopts->p11_module_name != NULL) free(idopts->p11_module_name); idopts->p11_module_name = strdup(vp); if (idopts->p11_module_name == NULL) goto cleanup; } else if (!strcmp(cp, "slotid")) { long slotid = strtol(vp, NULL, 10); if ((slotid == LONG_MIN || slotid == LONG_MAX) && errno != 0) { retval = EINVAL; goto cleanup; } if ((long) (int) slotid != slotid) { retval = EINVAL; goto cleanup; } idopts->slotid = slotid; } else if (!strcmp(cp, "token")) { if (idopts->token_label != NULL) free(idopts->token_label); idopts->token_label = strdup(vp); if (idopts->token_label == NULL) goto cleanup; } else if (!strcmp(cp, "certid")) { if (idopts->cert_id_string != NULL) free(idopts->cert_id_string); idopts->cert_id_string = strdup(vp); if (idopts->cert_id_string == NULL) goto cleanup; } else if (!strcmp(cp, "certlabel")) { if (idopts->cert_label != NULL) free(idopts->cert_label); idopts->cert_label = strdup(vp); if (idopts->cert_label == NULL) goto cleanup; } } retval = 0; cleanup: free(s); return retval; } #endif /* ARGSUSED */ static krb5_error_code parse_fs_options(krb5_context context, pkinit_identity_opts *idopts, const char *residual) { char *certname, *keyname; krb5_error_code retval = ENOMEM; if (residual == NULL || residual[0] == '\0') return 0; certname = strdup(residual); if (certname == NULL) goto cleanup; certname = strtok(certname, ","); keyname = strtok(NULL, ","); idopts->cert_filename = strdup(certname); if (idopts->cert_filename == NULL) goto cleanup; idopts->key_filename = strdup(keyname ? keyname : certname); if (idopts->key_filename == NULL) goto cleanup; retval = 0; cleanup: if (certname != NULL) free(certname); return retval; } /* ARGSUSED */ static krb5_error_code parse_pkcs12_options(krb5_context context, pkinit_identity_opts *idopts, const char *residual) { krb5_error_code retval = ENOMEM; if (residual == NULL || residual[0] == '\0') return 0; idopts->cert_filename = strdup(residual); if (idopts->cert_filename == NULL) goto cleanup; idopts->key_filename = strdup(residual); if (idopts->key_filename == NULL) goto cleanup; pkiDebug("%s: cert_filename '%s' key_filename '%s'\n", __FUNCTION__, idopts->cert_filename, idopts->key_filename); retval = 0; cleanup: return retval; } static krb5_error_code process_option_identity(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_opts *idopts, pkinit_identity_crypto_context id_cryptoctx, const char *value) { const char *residual; int idtype; krb5_error_code retval = 0; pkiDebug("%s: processing value '%s'\n", __FUNCTION__, value ? value : "NULL"); if (value == NULL) return EINVAL; residual = strchr(value, ':'); if (residual != NULL) { unsigned int typelen; residual++; /* skip past colon */ typelen = residual - value; if (strncmp(value, "FILE:", typelen) == 0) { idtype = IDTYPE_FILE; #ifndef WITHOUT_PKCS11 } else if (strncmp(value, "PKCS11:", typelen) == 0) { idtype = IDTYPE_PKCS11; #endif } else if (strncmp(value, "PKCS12:", typelen) == 0) { idtype = IDTYPE_PKCS12; } else if (strncmp(value, "DIR:", typelen) == 0) { idtype = IDTYPE_DIR; } else if (strncmp(value, "ENV:", typelen) == 0) { idtype = IDTYPE_ENVVAR; } else { pkiDebug("%s: Unsupported type while processing '%s'\n", __FUNCTION__, value); krb5_set_error_message(context, KRB5_PREAUTH_FAILED, "Unsupported type while processing '%s'\n", value); return KRB5_PREAUTH_FAILED; } } else { idtype = IDTYPE_FILE; residual = value; } idopts->idtype = idtype; pkiDebug("%s: idtype is %s\n", __FUNCTION__, idtype2string(idopts->idtype)); switch (idtype) { case IDTYPE_ENVVAR: { /* Solaris Kerberos: Improved error messages */ char *envvar = getenv(residual); if (envvar == NULL) { krb5_set_error_message(context, EINVAL, gettext("failed to find environmental variable \'%s\'"), residual); return EINVAL; } return process_option_identity(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, envvar); /* Solaris Kerberos: not reached */ } case IDTYPE_FILE: retval = parse_fs_options(context, idopts, residual); break; case IDTYPE_PKCS12: retval = parse_pkcs12_options(context, idopts, residual); break; #ifndef WITHOUT_PKCS11 case IDTYPE_PKCS11: retval = parse_pkcs11_options(context, idopts, residual); break; #endif case IDTYPE_DIR: idopts->cert_filename = strdup(residual); if (idopts->cert_filename == NULL) retval = ENOMEM; break; default: krb5_set_error_message(context, KRB5_PREAUTH_FAILED, "Internal error parsing X509_user_identity\n"); retval = EINVAL; break; } return retval; } static krb5_error_code process_option_ca_crl(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_opts *idopts, pkinit_identity_crypto_context id_cryptoctx, const char *value, int catype) { char *residual; unsigned int typelen; int idtype; pkiDebug("%s: processing catype %s, value '%s'\n", __FUNCTION__, catype2string(catype), value); residual = strchr(value, ':'); if (residual == NULL) { pkiDebug("No type given for '%s'\n", value); return EINVAL; } residual++; /* skip past colon */ typelen = residual - value; if (strncmp(value, "FILE:", typelen) == 0) { idtype = IDTYPE_FILE; } else if (strncmp(value, "DIR:", typelen) == 0) { idtype = IDTYPE_DIR; } else { return ENOTSUP; } return crypto_load_cas_and_crls(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, idtype, catype, residual); } static krb5_error_code pkinit_identity_process_option(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_opts *idopts, pkinit_identity_crypto_context id_cryptoctx, int attr, const char *value) { krb5_error_code retval = 0; switch (attr) { case PKINIT_ID_OPT_USER_IDENTITY: retval = process_option_identity(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, value); break; case PKINIT_ID_OPT_ANCHOR_CAS: retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, value, CATYPE_ANCHORS); break; case PKINIT_ID_OPT_INTERMEDIATE_CAS: retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, value, CATYPE_INTERMEDIATES); break; case PKINIT_ID_OPT_CRLS: retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, value, CATYPE_CRLS); break; case PKINIT_ID_OPT_OCSP: retval = ENOTSUP; break; default: retval = EINVAL; break; } return retval; } krb5_error_code pkinit_identity_initialize(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_opts *idopts, pkinit_identity_crypto_context id_cryptoctx, int do_matching, krb5_principal princ) { krb5_error_code retval = EINVAL; int i; pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx); if (idopts == NULL || id_cryptoctx == NULL) goto errout; /* * If identity was specified, use that. (For the kdc, this * is specified as pkinit_identity in the kdc.conf. For users, * this is specified on the command line via X509_user_identity.) * If a user did not specify identity on the command line, * then we will try alternatives which may have been specified * in the config file. */ if (idopts->identity != NULL) { retval = pkinit_identity_process_option(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, PKINIT_ID_OPT_USER_IDENTITY, idopts->identity); } else if (idopts->identity_alt != NULL) { for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++) retval = pkinit_identity_process_option(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, PKINIT_ID_OPT_USER_IDENTITY, idopts->identity_alt[i]); } else { pkiDebug("%s: no user identity options specified\n", __FUNCTION__); goto errout; } if (retval) goto errout; retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, princ, do_matching); if (retval) goto errout; if (do_matching) { retval = pkinit_cert_matching(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, princ, TRUE); if (retval) { pkiDebug("%s: No matching certificate found\n", __FUNCTION__); (void) crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx); goto errout; } } else { /* Tell crypto code to use the "default" */ retval = crypto_cert_select_default(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx); if (retval) { pkiDebug("%s: Failed while selecting default certificate\n", __FUNCTION__); (void) crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx); goto errout; } } retval = crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx); if (retval) goto errout; for (i = 0; idopts->anchors != NULL && idopts->anchors[i] != NULL; i++) { retval = pkinit_identity_process_option(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, PKINIT_ID_OPT_ANCHOR_CAS, idopts->anchors[i]); if (retval) goto errout; } for (i = 0; idopts->intermediates != NULL && idopts->intermediates[i] != NULL; i++) { retval = pkinit_identity_process_option(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, PKINIT_ID_OPT_INTERMEDIATE_CAS, idopts->intermediates[i]); if (retval) goto errout; } for (i = 0; idopts->crls != NULL && idopts->crls[i] != NULL; i++) { retval = pkinit_identity_process_option(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, PKINIT_ID_OPT_CRLS, idopts->crls[i]); if (retval) goto errout; } if (idopts->ocsp != NULL) { retval = pkinit_identity_process_option(context, plg_cryptoctx, req_cryptoctx, idopts, id_cryptoctx, PKINIT_ID_OPT_OCSP, idopts->ocsp); if (retval) goto errout; } errout: return retval; } /* * COPYRIGHT (C) 2006,2007 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN * ALL RIGHTS RESERVED * * Permission is granted to use, copy, create derivative works * and redistribute this software and such derivative works * for any purpose, so long as the name of The University of * Michigan is not used in any advertising or publicity * pertaining to the use of distribution of this software * without specific, written prior authorization. If the * above copyright notice or any other identification of the * University of Michigan is included in any copy of any * portion of this software, then the disclaimer below must * also be included. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF * SUCH DAMAGES. */ #include #include #include #include #include #include #include #include "pkinit.h" #define FAKECERT const krb5_octet_data dh_oid = { 0, 7, (unsigned char *)"\x2A\x86\x48\xce\x3e\x02\x01" }; krb5_error_code pkinit_init_req_opts(pkinit_req_opts **reqopts) { krb5_error_code retval = ENOMEM; pkinit_req_opts *opts = NULL; *reqopts = NULL; opts = (pkinit_req_opts *) calloc(1, sizeof(pkinit_req_opts)); if (opts == NULL) return retval; opts->require_eku = 1; opts->accept_secondary_eku = 0; opts->allow_upn = 0; opts->dh_or_rsa = DH_PROTOCOL; opts->require_crl_checking = 0; opts->dh_size = PKINIT_DEFAULT_DH_MIN_BITS; opts->win2k_target = 0; opts->win2k_require_cksum = 0; *reqopts = opts; return 0; } void pkinit_fini_req_opts(pkinit_req_opts *opts) { if (opts != NULL) free(opts); return; } krb5_error_code pkinit_init_plg_opts(pkinit_plg_opts **plgopts) { krb5_error_code retval = ENOMEM; pkinit_plg_opts *opts = NULL; *plgopts = NULL; opts = (pkinit_plg_opts *) calloc(1, sizeof(pkinit_plg_opts)); if (opts == NULL) return retval; opts->require_eku = 1; opts->accept_secondary_eku = 0; opts->dh_or_rsa = DH_PROTOCOL; opts->allow_upn = 0; opts->require_crl_checking = 0; opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS; *plgopts = opts; return 0; } void pkinit_fini_plg_opts(pkinit_plg_opts *opts) { if (opts != NULL) free(opts); return; } void free_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in) { if (*in == NULL) return; if ((*in)->signedAuthPack.data != NULL) free((*in)->signedAuthPack.data); if ((*in)->trustedCertifiers != NULL) free_krb5_external_principal_identifier(&(*in)->trustedCertifiers); if ((*in)->kdcPkId.data != NULL) free((*in)->kdcPkId.data); free(*in); } void free_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in) { if (*in == NULL) return; if ((*in)->signedAuthPack.data != NULL) free((*in)->signedAuthPack.data); if ((*in)->kdcCert.data != NULL) free((*in)->kdcCert.data); if ((*in)->encryptionCert.data != NULL) free((*in)->encryptionCert.data); if ((*in)->trustedCertifiers != NULL) free_krb5_trusted_ca(&(*in)->trustedCertifiers); free(*in); } void free_krb5_reply_key_pack(krb5_reply_key_pack **in) { if (*in == NULL) return; if ((*in)->replyKey.contents != NULL) free((*in)->replyKey.contents); if ((*in)->asChecksum.contents != NULL) free((*in)->asChecksum.contents); free(*in); } void free_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in) { if (*in == NULL) return; if ((*in)->replyKey.contents != NULL) free((*in)->replyKey.contents); free(*in); } void free_krb5_auth_pack(krb5_auth_pack **in) { if ((*in) == NULL) return; if ((*in)->clientPublicValue != NULL) { if ((*in)->clientPublicValue->algorithm.algorithm.data != NULL) free((*in)->clientPublicValue->algorithm.algorithm.data); if ((*in)->clientPublicValue->algorithm.parameters.data != NULL) free((*in)->clientPublicValue->algorithm.parameters.data); if ((*in)->clientPublicValue->subjectPublicKey.data != NULL) free((*in)->clientPublicValue->subjectPublicKey.data); free((*in)->clientPublicValue); } if ((*in)->pkAuthenticator.paChecksum.contents != NULL) free((*in)->pkAuthenticator.paChecksum.contents); if ((*in)->supportedCMSTypes != NULL) free_krb5_algorithm_identifiers(&((*in)->supportedCMSTypes)); free(*in); } void free_krb5_auth_pack_draft9(krb5_context context, krb5_auth_pack_draft9 **in) { if ((*in) == NULL) return; krb5_free_principal(context, (*in)->pkAuthenticator.kdcName); free(*in); } void free_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in) { if (*in == NULL) return; switch ((*in)->choice) { case choice_pa_pk_as_rep_dhInfo: if ((*in)->u.dh_Info.dhSignedData.data != NULL) free((*in)->u.dh_Info.dhSignedData.data); break; case choice_pa_pk_as_rep_encKeyPack: if ((*in)->u.encKeyPack.data != NULL) free((*in)->u.encKeyPack.data); break; default: break; } free(*in); } void free_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in) { if (*in == NULL) return; if ((*in)->u.encKeyPack.data != NULL) free((*in)->u.encKeyPack.data); free(*in); } void free_krb5_external_principal_identifier(krb5_external_principal_identifier ***in) { int i = 0; if (*in == NULL) return; while ((*in)[i] != NULL) { if ((*in)[i]->subjectName.data != NULL) free((*in)[i]->subjectName.data); if ((*in)[i]->issuerAndSerialNumber.data != NULL) free((*in)[i]->issuerAndSerialNumber.data); if ((*in)[i]->subjectKeyIdentifier.data != NULL) free((*in)[i]->subjectKeyIdentifier.data); free((*in)[i]); i++; } free(*in); } void free_krb5_trusted_ca(krb5_trusted_ca ***in) { int i = 0; if (*in == NULL) return; while ((*in)[i] != NULL) { switch((*in)[i]->choice) { case choice_trusted_cas_principalName: break; case choice_trusted_cas_caName: if ((*in)[i]->u.caName.data != NULL) free((*in)[i]->u.caName.data); break; case choice_trusted_cas_issuerAndSerial: if ((*in)[i]->u.issuerAndSerial.data != NULL) free((*in)[i]->u.issuerAndSerial.data); break; case choice_trusted_cas_UNKNOWN: break; } free((*in)[i]); i++; } free(*in); } void free_krb5_typed_data(krb5_typed_data ***in) { int i = 0; if (*in == NULL) return; while ((*in)[i] != NULL) { if ((*in)[i]->data != NULL) free((*in)[i]->data); free((*in)[i]); i++; } free(*in); } void free_krb5_algorithm_identifier(krb5_algorithm_identifier *in) { if (in == NULL) return; if (in->algorithm.data != NULL) free(in->algorithm.data); if (in->parameters.data != NULL) free(in->parameters.data); free(in); } void free_krb5_algorithm_identifiers(krb5_algorithm_identifier ***in) { int i; if (in == NULL || *in == NULL) return; for (i = 0; (*in)[i] != NULL; i++) { free_krb5_algorithm_identifier((*in)[i]); } free(*in); } void free_krb5_subject_pk_info(krb5_subject_pk_info **in) { if ((*in) == NULL) return; if ((*in)->algorithm.parameters.data != NULL) free((*in)->algorithm.parameters.data); if ((*in)->subjectPublicKey.data != NULL) free((*in)->subjectPublicKey.data); free(*in); } void free_krb5_kdc_dh_key_info(krb5_kdc_dh_key_info **in) { if (*in == NULL) return; if ((*in)->subjectPublicKey.data != NULL) free((*in)->subjectPublicKey.data); free(*in); } void init_krb5_pa_pk_as_req(krb5_pa_pk_as_req **in) { (*in) = malloc(sizeof(krb5_pa_pk_as_req)); if ((*in) == NULL) return; (*in)->signedAuthPack.data = NULL; (*in)->signedAuthPack.length = 0; (*in)->trustedCertifiers = NULL; (*in)->kdcPkId.data = NULL; (*in)->kdcPkId.length = 0; } void init_krb5_pa_pk_as_req_draft9(krb5_pa_pk_as_req_draft9 **in) { (*in) = malloc(sizeof(krb5_pa_pk_as_req_draft9)); if ((*in) == NULL) return; (*in)->signedAuthPack.data = NULL; (*in)->signedAuthPack.length = 0; (*in)->trustedCertifiers = NULL; (*in)->kdcCert.data = NULL; (*in)->kdcCert.length = 0; (*in)->encryptionCert.data = NULL; (*in)->encryptionCert.length = 0; } void init_krb5_reply_key_pack(krb5_reply_key_pack **in) { (*in) = malloc(sizeof(krb5_reply_key_pack)); if ((*in) == NULL) return; (*in)->replyKey.contents = NULL; (*in)->replyKey.length = 0; (*in)->asChecksum.contents = NULL; (*in)->asChecksum.length = 0; } void init_krb5_reply_key_pack_draft9(krb5_reply_key_pack_draft9 **in) { (*in) = malloc(sizeof(krb5_reply_key_pack_draft9)); if ((*in) == NULL) return; (*in)->replyKey.contents = NULL; (*in)->replyKey.length = 0; } void init_krb5_auth_pack(krb5_auth_pack **in) { (*in) = malloc(sizeof(krb5_auth_pack)); if ((*in) == NULL) return; (*in)->clientPublicValue = NULL; (*in)->supportedCMSTypes = NULL; (*in)->clientDHNonce.length = 0; (*in)->clientDHNonce.data = NULL; (*in)->pkAuthenticator.paChecksum.contents = NULL; } void init_krb5_auth_pack_draft9(krb5_auth_pack_draft9 **in) { (*in) = malloc(sizeof(krb5_auth_pack_draft9)); if ((*in) == NULL) return; (*in)->clientPublicValue = NULL; } void init_krb5_pa_pk_as_rep(krb5_pa_pk_as_rep **in) { (*in) = malloc(sizeof(krb5_pa_pk_as_rep)); if ((*in) == NULL) return; (*in)->u.dh_Info.serverDHNonce.length = 0; (*in)->u.dh_Info.serverDHNonce.data = NULL; (*in)->u.dh_Info.dhSignedData.length = 0; (*in)->u.dh_Info.dhSignedData.data = NULL; (*in)->u.encKeyPack.length = 0; (*in)->u.encKeyPack.data = NULL; } void init_krb5_pa_pk_as_rep_draft9(krb5_pa_pk_as_rep_draft9 **in) { (*in) = malloc(sizeof(krb5_pa_pk_as_rep_draft9)); if ((*in) == NULL) return; (*in)->u.dhSignedData.length = 0; (*in)->u.dhSignedData.data = NULL; (*in)->u.encKeyPack.length = 0; (*in)->u.encKeyPack.data = NULL; } void init_krb5_typed_data(krb5_typed_data **in) { (*in) = malloc(sizeof(krb5_typed_data)); if ((*in) == NULL) return; (*in)->type = 0; (*in)->length = 0; (*in)->data = NULL; } void init_krb5_subject_pk_info(krb5_subject_pk_info **in) { (*in) = malloc(sizeof(krb5_subject_pk_info)); if ((*in) == NULL) return; (*in)->algorithm.parameters.data = NULL; (*in)->algorithm.parameters.length = 0; (*in)->subjectPublicKey.data = NULL; (*in)->subjectPublicKey.length = 0; } krb5_error_code pkinit_copy_krb5_octet_data(krb5_octet_data *dst, const krb5_octet_data *src) { if (dst == NULL || src == NULL) return EINVAL; if (src->data == NULL) { dst->data = NULL; dst->length = 0; return 0; } dst->data = malloc(src->length); if (dst->data == NULL) return ENOMEM; (void) memcpy(dst->data, src->data, src->length); dst->length = src->length; return 0; } /* debugging functions */ void print_buffer(unsigned char *buf, unsigned int len) { int i = 0; /* Solaris Kerberos: len is unsigned (lint) */ if (len == 0) return; for (i = 0; i < len; i++) pkiDebug("%02x ", buf[i]); pkiDebug("\n"); } void print_buffer_bin(unsigned char *buf, unsigned int len, char *filename) { FILE *f = NULL; int i = 0; /* Solaris Kerberos: len is unsigned (lint) */ if (len == 0 || filename == NULL) return; if ((f = fopen(filename, "w")) == NULL) return; for (i = 0; i < len; i++) (void) fputc(buf[i], f); (void) fclose(f); } /* * COPYRIGHT (C) 2007 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN * ALL RIGHTS RESERVED * * Permission is granted to use, copy, create derivative works * and redistribute this software and such derivative works * for any purpose, so long as the name of The University of * Michigan is not used in any advertising or publicity * pertaining to the use of distribution of this software * without specific, written prior authorization. If the * above copyright notice or any other identification of the * University of Michigan is included in any copy of any * portion of this software, then the disclaimer below must * also be included. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF * SUCH DAMAGES. */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include "pkinit.h" typedef struct _pkinit_cert_info pkinit_cert_info; typedef enum { kw_undefined = 0, kw_subject = 1, kw_issuer = 2, kw_san = 3, kw_eku = 4, kw_ku = 5 } keyword_type; static char * keyword2string(unsigned int kw) { /* Solaris Kerberos: removed "break"s (lint) */ switch(kw) { case kw_undefined: return "NONE"; case kw_subject: return "SUBJECT"; case kw_issuer: return "ISSUER"; case kw_san: return "SAN"; case kw_eku: return "EKU"; case kw_ku: return "KU"; default: return "INVALID"; } } typedef enum { relation_none = 0, relation_and = 1, relation_or = 2 } relation_type; static char * relation2string(unsigned int rel) { /* Solaris Kerberos: removed "break"s (lint) */ switch(rel) { case relation_none: return "NONE"; case relation_and: return "AND"; case relation_or: return "OR"; default: return "INVALID"; } } typedef enum { kwvaltype_undefined = 0, kwvaltype_regexp = 1, kwvaltype_list = 2 } kw_value_type; static char * kwval2string(unsigned int kwval) { /* Solaris Kerberos: removed "break"s (lint) */ switch(kwval) { case kwvaltype_undefined: return "NONE"; case kwvaltype_regexp: return "REGEXP"; case kwvaltype_list: return "LIST"; default: return "INVALID"; } } struct keyword_desc { const char *value; size_t length; keyword_type kwtype; kw_value_type kwvaltype; } matching_keywords[] = { { "", 4, kw_ku, kwvaltype_list }, { "", 5, kw_eku, kwvaltype_list }, { "", 5, kw_san, kwvaltype_regexp }, { "", 8, kw_issuer, kwvaltype_regexp }, { "", 9, kw_subject, kwvaltype_regexp }, { NULL, 0, kw_undefined, kwvaltype_undefined}, }; struct ku_desc { const char *value; size_t length; unsigned int bitval; }; struct ku_desc ku_keywords[] = { { "digitalSignature", 16, PKINIT_KU_DIGITALSIGNATURE }, { "keyEncipherment", 15, PKINIT_KU_KEYENCIPHERMENT }, { NULL, 0, 0 }, }; struct ku_desc eku_keywords[] = { { "pkinit", 6, PKINIT_EKU_PKINIT }, { "msScLogin", 9, PKINIT_EKU_MSSCLOGIN }, { "clientAuth", 10, PKINIT_EKU_CLIENTAUTH }, { "emailProtection", 15, PKINIT_EKU_EMAILPROTECTION }, { NULL, 0, 0 }, }; /* Rule component */ typedef struct _rule_component { struct _rule_component *next; keyword_type kw_type; kw_value_type kwval_type; regex_t regexp; /* Compiled regular expression */ char *regsrc; /* The regular expression source (for debugging) */ unsigned int ku_bits; unsigned int eku_bits; } rule_component; /* Set rule components */ typedef struct _rule_set { relation_type relation; int num_crs; rule_component *crs; } rule_set; /* ARGSUSED */ static krb5_error_code free_rule_component(krb5_context context, rule_component *rc) { if (rc == NULL) return 0; if (rc->kwval_type == kwvaltype_regexp) { if (rc->regsrc) free(rc->regsrc); regfree(&rc->regexp); } free(rc); return 0; } static krb5_error_code free_rule_set(krb5_context context, rule_set *rs) { rule_component *rc, *trc; if (rs == NULL) return 0; for (rc = rs->crs; rc != NULL;) { trc = rc->next; /* Solaris Kerberos */ (void) free_rule_component(context, rc); rc = trc; } free(rs); return 0; } /* ARGSUSED */ static krb5_error_code parse_list_value(krb5_context context, keyword_type type, char *value, rule_component *rc) { krb5_error_code retval; char *comma; struct ku_desc *ku = NULL; int found; size_t len; unsigned int *bitptr; if (value == NULL || value[0] == '\0') { pkiDebug("%s: Missing or empty value for list keyword type %d\n", __FUNCTION__, type); retval = EINVAL; goto out; } if (type == kw_eku) { bitptr = &rc->eku_bits; } else if (type == kw_ku) { bitptr = &rc->ku_bits; } else { pkiDebug("%s: Unknown list keyword type %d\n", __FUNCTION__, type); retval = EINVAL; goto out; } do { found = 0; comma = strchr(value, ','); if (comma != NULL) len = comma - value; else len = strlen(value); if (type == kw_eku) { ku = eku_keywords; } else if (type == kw_ku) { ku = ku_keywords; } for (; ku->value != NULL; ku++) { if (strncasecmp(value, ku->value, len) == 0) { *bitptr |= ku->bitval; found = 1; pkiDebug("%s: Found value '%s', bitfield is now 0x%x\n", __FUNCTION__, ku->value, *bitptr); break; } } if (found) { value += ku->length; if (*value == ',') value += 1; } else { pkiDebug("%s: Urecognized value '%s'\n", __FUNCTION__, value); retval = EINVAL; goto out; } } while (found && *value != '\0'); retval = 0; out: pkiDebug("%s: returning %d\n", __FUNCTION__, retval); return retval; } static krb5_error_code parse_rule_component(krb5_context context, const char **rule, int *remaining, rule_component **ret_rule) { krb5_error_code retval; rule_component *rc = NULL; keyword_type kw_type; kw_value_type kwval_type; char err_buf[128]; int ret; struct keyword_desc *kw, *nextkw; char *nk; int found_next_kw = 0; char *value = NULL; size_t len; for (kw = matching_keywords; kw->value != NULL; kw++) { if (strncmp(*rule, kw->value, kw->length) == 0) { kw_type = kw->kwtype; kwval_type = kw->kwvaltype; *rule += kw->length; *remaining -= kw->length; break; } } if (kw->value == NULL) { pkiDebug("%s: Missing or invalid keyword in rule '%s'\n", __FUNCTION__, *rule); retval = ENOENT; goto out; } pkiDebug("%s: found keyword '%s'\n", __FUNCTION__, kw->value); rc = calloc(1, sizeof(*rc)); if (rc == NULL) { retval = ENOMEM; goto out; } rc->next = NULL; rc->kw_type = kw_type; rc->kwval_type = kwval_type; /* * Before procesing the value for this keyword, * (compiling the regular expression or processing the list) * we need to find the end of it. That means parsing for the * beginning of the next keyword (or the end of the rule). */ nk = strchr(*rule, '<'); while (nk != NULL) { /* Possibly another keyword, check it out */ for (nextkw = matching_keywords; nextkw->value != NULL; nextkw++) { if (strncmp(nk, nextkw->value, nextkw->length) == 0) { /* Found a keyword, nk points to the beginning */ found_next_kw = 1; break; /* Need to break out of the while! */ } } if (!found_next_kw) nk = strchr(nk+1, '<'); /* keep looking */ else break; } if (nk != NULL && found_next_kw) len = (nk - *rule); else len = (*remaining); if (len == 0) { pkiDebug("%s: Missing value for keyword '%s'\n", __FUNCTION__, kw->value); retval = EINVAL; goto out; } value = calloc(1, len+1); if (value == NULL) { retval = ENOMEM; goto out; } (void) memcpy(value, *rule, len); *remaining -= len; *rule += len; pkiDebug("%s: found value '%s'\n", __FUNCTION__, value); if (kw->kwvaltype == kwvaltype_regexp) { ret = regcomp(&rc->regexp, value, REG_EXTENDED); if (ret) { (void) regerror(ret, &rc->regexp, err_buf, sizeof(err_buf)); pkiDebug("%s: Error compiling reg-exp '%s': %s\n", __FUNCTION__, value, err_buf); retval = ret; goto out; } rc->regsrc = strdup(value); if (rc->regsrc == NULL) { retval = ENOMEM; goto out; } } else if (kw->kwvaltype == kwvaltype_list) { retval = parse_list_value(context, rc->kw_type, value, rc); if (retval) { pkiDebug("%s: Error %d, parsing list values for keyword %s\n", __FUNCTION__, retval, kw->value); goto out; } } *ret_rule = rc; retval = 0; out: if (value != NULL) free(value); if (retval && rc != NULL) (void) free_rule_component(context, rc); pkiDebug("%s: returning %d\n", __FUNCTION__, retval); return retval; } /* ARGSUSED */ static krb5_error_code parse_rule_set(krb5_context context, const char *rule_in, rule_set **out_rs) { const char *rule; /* Solaris Kerberos */ int remaining; krb5_error_code ret, retval; rule_component *rc = NULL, *trc; rule_set *rs; if (rule_in == NULL) return EINVAL; rule = rule_in; /* Solaris Kerberos */ remaining = strlen(rule); rs = calloc(1, sizeof(*rs)); if (rs == NULL) { retval = ENOMEM; goto cleanup; } rs->relation = relation_none; if (remaining > 1) { if (rule[0] == '&' && rule[1] == '&') { rs->relation = relation_and; rule += 2; remaining -= 2; } else if (rule_in[0] == '|' && rule_in[1] == '|') { rs->relation = relation_or; rule +=2; remaining -= 2; } } rs->num_crs = 0; while (remaining > 0) { if (rs->relation == relation_none && rs->num_crs > 1) { pkiDebug("%s: Assuming AND relation for multiple components in rule '%s'\n", __FUNCTION__, rule_in); rs->relation = relation_and; } ret = parse_rule_component(context, &rule, &remaining, &rc); if (ret) { retval = ret; goto cleanup; } pkiDebug("%s: After parse_rule_component, remaining %d, rule '%s'\n", __FUNCTION__, remaining, rule); rs->num_crs++; /* * Chain the new component on the end (order matters since * we can short-circuit an OR or an AND relation if an * earlier check passes */ for (trc = rs->crs; trc != NULL && trc->next != NULL; trc = trc->next); if (trc == NULL) rs->crs = rc; else { trc->next = rc; } } *out_rs = rs; retval = 0; cleanup: if (retval && rs != NULL) { (void) free_rule_set(context, rs); } pkiDebug("%s: returning %d\n", __FUNCTION__, retval); return retval; } /* ARGSUSED */ static int regexp_match(krb5_context context, rule_component *rc, char *value) { int code; pkiDebug("%s: checking %s rule '%s' with value '%s'\n", __FUNCTION__, keyword2string(rc->kw_type), rc->regsrc, value); code = regexec(&rc->regexp, value, 0, NULL, 0); pkiDebug("%s: the result is%s a match\n", __FUNCTION__, code == REG_NOMATCH ? " NOT" : ""); return (code == 0 ? 1: 0); } static int component_match(krb5_context context, rule_component *rc, pkinit_cert_matching_data *md) { int match = 0; int i; krb5_principal p; char *princ_string; switch (rc->kwval_type) { case kwvaltype_regexp: switch (rc->kw_type) { case kw_subject: match = regexp_match(context, rc, md->subject_dn); break; case kw_issuer: match = regexp_match(context, rc, md->issuer_dn); break; case kw_san: if (md->sans == NULL) break; for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i]) { krb5_unparse_name(context, p, &princ_string); match = regexp_match(context, rc, princ_string); krb5_free_unparsed_name(context, princ_string); if (match) break; } break; default: pkiDebug("%s: keyword %s, keyword value %s mismatch\n", __FUNCTION__, keyword2string(rc->kw_type), kwval2string(kwvaltype_regexp)); break; } break; case kwvaltype_list: switch(rc->kw_type) { case kw_eku: pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n", __FUNCTION__, keyword2string(rc->kw_type), rc->eku_bits, md->eku_bits); if ((rc->eku_bits & md->eku_bits) == rc->eku_bits) match = 1; break; case kw_ku: pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n", __FUNCTION__, keyword2string(rc->kw_type), rc->ku_bits, md->ku_bits); if ((rc->ku_bits & md->ku_bits) == rc->ku_bits) match = 1; break; default: pkiDebug("%s: keyword %s, keyword value %s mismatch\n", __FUNCTION__, keyword2string(rc->kw_type), kwval2string(kwvaltype_regexp)); break; } break; default: pkiDebug("%s: unknown keyword value type %d\n", __FUNCTION__, rc->kwval_type); break; } pkiDebug("%s: returning match = %d\n", __FUNCTION__, match); return match; } /* * Returns match_found == 1 only if exactly one certificate matches * the given rule */ /* ARGSUSED */ static krb5_error_code check_all_certs(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_principal princ, rule_set *rs, /* rule to check */ pkinit_cert_matching_data **matchdata, int *match_found, pkinit_cert_matching_data **matching_cert) { krb5_error_code retval; pkinit_cert_matching_data *md; int i; int comp_match = 0; int total_cert_matches = 0; rule_component *rc; int certs_checked = 0; pkinit_cert_matching_data *save_match = NULL; if (match_found == NULL || matching_cert == NULL) return EINVAL; *matching_cert = NULL; *match_found = 0; pkiDebug("%s: matching rule relation is %s with %d components\n", __FUNCTION__, relation2string(rs->relation), rs->num_crs); /* * Loop through all the certs available and count * how many match the rule */ for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) { pkiDebug("%s: subject: '%s'\n", __FUNCTION__, md->subject_dn); #if 0 pkiDebug("%s: issuer: '%s'\n", __FUNCTION__, md->subject_dn); for (j = 0, p = md->sans[j]; p != NULL; p = md->sans[++j]) { char *san_string; krb5_unparse_name(context, p, &san_string); pkiDebug("%s: san: '%s'\n", __FUNCTION__, san_string); krb5_free_unparsed_name(context, san_string); } #endif certs_checked++; for (rc = rs->crs; rc != NULL; rc = rc->next) { comp_match = component_match(context, rc, md); if (comp_match) { pkiDebug("%s: match for keyword type %s\n", __FUNCTION__, keyword2string(rc->kw_type)); } if (comp_match && rs->relation == relation_or) { pkiDebug("%s: cert matches rule (OR relation)\n", __FUNCTION__); total_cert_matches++; save_match = md; goto nextcert; } if (!comp_match && rs->relation == relation_and) { pkiDebug("%s: cert does not match rule (AND relation)\n", __FUNCTION__); goto nextcert; } } if (rc == NULL && comp_match) { pkiDebug("%s: cert matches rule (AND relation)\n", __FUNCTION__); total_cert_matches++; save_match = md; } nextcert: continue; } pkiDebug("%s: After checking %d certs, we found %d matches\n", __FUNCTION__, certs_checked, total_cert_matches); if (total_cert_matches == 1) { *match_found = 1; *matching_cert = save_match; } retval = 0; pkiDebug("%s: returning %d, match_found %d\n", __FUNCTION__, retval, *match_found); return retval; } static krb5_error_code free_all_cert_matching_data(krb5_context context, pkinit_cert_matching_data **matchdata) { krb5_error_code retval; pkinit_cert_matching_data *md; int i; if (matchdata == NULL) return EINVAL; for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) { pkinit_cert_handle ch = md->ch; retval = crypto_cert_free_matching_data(context, md); if (retval) { pkiDebug("%s: crypto_cert_free_matching_data error %d, %s\n", __FUNCTION__, retval, error_message(retval)); goto cleanup; } retval = crypto_cert_release(context, ch); if (retval) { pkiDebug("%s: crypto_cert_release error %d, %s\n", __FUNCTION__, retval, error_message(retval)); goto cleanup; } } free(matchdata); retval = 0; cleanup: return retval; } static krb5_error_code obtain_all_cert_matching_data(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, pkinit_cert_matching_data ***all_matching_data) { krb5_error_code retval; int i, cert_count; pkinit_cert_iter_handle ih = NULL; pkinit_cert_handle ch; pkinit_cert_matching_data **matchdata = NULL; retval = crypto_cert_get_count(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, &cert_count); if (retval) { pkiDebug("%s: crypto_cert_get_count error %d, %s\n", __FUNCTION__, retval, error_message(retval)); goto cleanup; } pkiDebug("%s: crypto_cert_get_count says there are %d certs\n", __FUNCTION__, cert_count); matchdata = calloc((size_t)cert_count + 1, sizeof(*matchdata)); if (matchdata == NULL) return ENOMEM; retval = crypto_cert_iteration_begin(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, &ih); if (retval) { pkiDebug("%s: crypto_cert_iteration_begin returned %d, %s\n", __FUNCTION__, retval, error_message(retval)); goto cleanup; } for (i = 0; i < cert_count; i++) { retval = crypto_cert_iteration_next(context, ih, &ch); if (retval) { if (retval == PKINIT_ITER_NO_MORE) pkiDebug("%s: We thought there were %d certs, but " "crypto_cert_iteration_next stopped after %d?\n", __FUNCTION__, cert_count, i); else pkiDebug("%s: crypto_cert_iteration_next error %d, %s\n", __FUNCTION__, retval, error_message(retval)); goto cleanup; } retval = crypto_cert_get_matching_data(context, ch, &matchdata[i]); if (retval) { pkiDebug("%s: crypto_cert_get_matching_data error %d, %s\n", __FUNCTION__, retval, error_message(retval)); goto cleanup; } } *all_matching_data = matchdata; retval = 0; cleanup: if (ih != NULL) /* Solaris Kerberos */ (void) crypto_cert_iteration_end(context, ih); if (retval) { if (matchdata != NULL) (void) free_all_cert_matching_data(context, matchdata); } pkiDebug("%s: returning %d, certinfo %p\n", __FUNCTION__, retval, *all_matching_data); return retval; } krb5_error_code pkinit_cert_matching(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, krb5_principal princ, krb5_boolean do_select) { krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; int x; char **rules = NULL; rule_set *rs = NULL; int match_found = 0; pkinit_cert_matching_data **matchdata = NULL; pkinit_cert_matching_data *the_matching_cert = NULL; /* If no matching rules, select the default cert and we're done */ (void) pkinit_libdefault_strings(context, krb5_princ_realm(context, princ), "pkinit_cert_match", &rules); if (rules == NULL) { pkiDebug("%s: no matching rules found in config file\n", __FUNCTION__); if (do_select == TRUE) { retval = crypto_cert_select_default(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx); } else retval = 0; goto cleanup; } /* parse each rule line one at a time and check all the certs against it */ for (x = 0; rules[x] != NULL; x++) { pkiDebug("%s: Processing rule '%s'\n", __FUNCTION__, rules[x]); /* Free rules from previous time through... */ if (rs != NULL) { (void) free_rule_set(context, rs); rs = NULL; } retval = parse_rule_set(context, rules[x], &rs); if (retval) { if (retval == EINVAL) { pkiDebug("%s: Ignoring invalid rule pkinit_cert_match = '%s'\n", __FUNCTION__, rules[x]); continue; } goto cleanup; } /* * Optimize so that we do not get cert info unless we have * valid rules to check. Once obtained, keep it around * until we are done. */ if (matchdata == NULL) { retval = obtain_all_cert_matching_data(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, &matchdata); if (retval || matchdata == NULL) { pkiDebug("%s: Error %d obtaining certificate information\n", __FUNCTION__, retval); retval = ENOENT; goto cleanup; } } retval = check_all_certs(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, princ, rs, matchdata, &match_found, &the_matching_cert); if (retval) { pkiDebug("%s: Error %d, checking certs against rule '%s'\n", __FUNCTION__, retval, rules[x]); goto cleanup; } if (match_found) { pkiDebug("%s: We have an exact match with rule '%s'\n", __FUNCTION__, rules[x]); break; } } if (match_found && the_matching_cert != NULL) { if (do_select == TRUE) { pkiDebug("%s: Selecting the matching cert!\n", __FUNCTION__); retval = crypto_cert_select(context, the_matching_cert); if (retval) { pkiDebug("%s: crypto_cert_select error %d, %s\n", __FUNCTION__, retval, error_message(retval)); goto cleanup; } } } else { retval = ENOENT; /* XXX */ goto cleanup; } retval = 0; cleanup: if (rules != NULL) profile_free_list(rules); if (rs != NULL) /* Solaris Kerberos */ (void) free_rule_set(context, rs); if (matchdata != NULL) (void) free_all_cert_matching_data(context, matchdata); return retval; } /* * COPYRIGHT (C) 2006,2007 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN * ALL RIGHTS RESERVED * * Permission is granted to use, copy, create derivative works * and redistribute this software and such derivative works * for any purpose, so long as the name of The University of * Michigan is not used in any advertising or publicity * pertaining to the use of distribution of this software * without specific, written prior authorization. If the * above copyright notice or any other identification of the * University of Michigan is included in any copy of any * portion of this software, then the disclaimer below must * also be included. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF * SUCH DAMAGES. */ #include #include #include #include #include "k5-int.h" #include "pkinit.h" /* * Routines for handling profile [config file] options */ /* Forward prototypes */ static int _krb5_conf_boolean(const char *s); /* * XXX * The following is duplicated verbatim from src/lib/krb5/krb/get_in_tkt.c, * which is duplicated from somewhere else. :-/ * XXX */ static const char *const conf_yes[] = { "y", "yes", "true", "t", "1", "on", 0, }; static const char *const conf_no[] = { "n", "no", "false", "nil", "0", "off", 0, }; static int _krb5_conf_boolean(const char *s) { const char *const *p; for(p=conf_yes; *p; p++) { if (strcasecmp(*p,s) == 0) return 1; } for(p=conf_no; *p; p++) { if (strcasecmp(*p,s) == 0) return 0; } /* Default to "no" */ return 0; } /* * XXX * End duplicated code from src/lib/krb5/krb/get_in_tkt.c * XXX */ /* * The following are based on krb5_libdefault_* functions in * src/lib/krb5/krb/get_in_tkt.c * N.B. This assumes that context->default_realm has * already been established. */ krb5_error_code pkinit_kdcdefault_strings(krb5_context context, const char *realmname, const char *option, char ***ret_value) { profile_t profile = NULL; const char *names[5]; char **values = NULL; krb5_error_code retval; if (context == NULL) return KV5M_CONTEXT; profile = context->profile; if (realmname != NULL) { /* * Try number one: * * [realms] * REALM = { * option = * } */ names[0] = "realms"; names[1] = realmname; names[2] = option; names[3] = 0; retval = profile_get_values(profile, names, &values); if (retval == 0 && values != NULL) goto goodbye; } /* * Try number two: * * [kdcdefaults] * option = */ names[0] = "kdcdefaults"; names[1] = option; names[2] = 0; retval = profile_get_values(profile, names, &values); if (retval == 0 && values != NULL) goto goodbye; goodbye: if (values == NULL) retval = ENOENT; *ret_value = values; return retval; } krb5_error_code pkinit_kdcdefault_string(krb5_context context, const char *realmname, const char *option, char **ret_value) { krb5_error_code retval; char **values = NULL; retval = pkinit_kdcdefault_strings(context, realmname, option, &values); if (retval) return retval; if (values[0] == NULL) { retval = ENOENT; } else { *ret_value = malloc(strlen(values[0]) + 1); /* Solaris Kerberos */ if (*ret_value == NULL) { pkiDebug(error_message(ENOMEM)); retval = ENOMEM; } else /* Solaris Kerberos */ (void) strlcpy(*ret_value, values[0], strlen(values[0]) + 1); } profile_free_list(values); return retval; } krb5_error_code pkinit_kdcdefault_boolean(krb5_context context, const char *realmname, const char *option, int default_value, int *ret_value) { char *string = NULL; krb5_error_code retval; retval = pkinit_kdcdefault_string(context, realmname, option, &string); if (retval == 0) { *ret_value = _krb5_conf_boolean(string); free(string); } else *ret_value = default_value; return 0; } krb5_error_code pkinit_kdcdefault_integer(krb5_context context, const char *realmname, const char *option, int default_value, int *ret_value) { char *string = NULL; krb5_error_code retval; retval = pkinit_kdcdefault_string(context, realmname, option, &string); if (retval == 0) { char *endptr; long l; l = strtol(string, &endptr, 0); if (endptr == string) *ret_value = default_value; else *ret_value = l; free(string); } else *ret_value = default_value; return 0; } /* * krb5_libdefault_string() is defined as static in * src/lib/krb5/krb/get_in_tkt.c. Create local versions of * krb5_libdefault_* functions here. We need a libdefaults_strings() * function which is not currently supported there anyway. Also, * add the ability to supply a default value for the boolean and * integer functions. */ krb5_error_code pkinit_libdefault_strings(krb5_context context, const krb5_data *realm, const char *option, char ***ret_value) { profile_t profile; const char *names[5]; char **values = NULL; krb5_error_code retval; char realmstr[1024]; if (realm != NULL && realm->length > sizeof(realmstr)-1) return EINVAL; if (realm != NULL) { /* Solaris Kerberos */ (void) strlcpy(realmstr, realm->data, realm->length + 1); realmstr[realm->length] = '\0'; } if (!context || (context->magic != KV5M_CONTEXT)) return KV5M_CONTEXT; profile = context->profile; if (realm != NULL) { /* * Try number one: * * [libdefaults] * REALM = { * option = * } */ names[0] = "libdefaults"; names[1] = realmstr; names[2] = option; names[3] = 0; retval = profile_get_values(profile, names, &values); if (retval == 0 && values != NULL && values[0] != NULL) goto goodbye; /* * Try number two: * * [realms] * REALM = { * option = * } */ names[0] = "realms"; names[1] = realmstr; names[2] = option; names[3] = 0; retval = profile_get_values(profile, names, &values); if (retval == 0 && values != NULL && values[0] != NULL) goto goodbye; } /* * Try number three: * * [libdefaults] * option = */ names[0] = "libdefaults"; names[1] = option; names[2] = 0; retval = profile_get_values(profile, names, &values); if (retval == 0 && values != NULL && values[0] != NULL) goto goodbye; goodbye: if (values == NULL) return ENOENT; *ret_value = values; return retval; } krb5_error_code pkinit_libdefault_string(krb5_context context, const krb5_data *realm, const char *option, char **ret_value) { krb5_error_code retval; char **values = NULL; retval = pkinit_libdefault_strings(context, realm, option, &values); if (retval) return retval; if (values[0] == NULL) { retval = ENOENT; } else { *ret_value = malloc(strlen(values[0]) + 1); if (*ret_value == NULL) retval = ENOMEM; else /* Solaris Kerberos */ (void) strlcpy(*ret_value, values[0], strlen(values[0]) + 1); } profile_free_list(values); return retval; } krb5_error_code pkinit_libdefault_boolean(krb5_context context, const krb5_data *realm, const char *option, int default_value, int *ret_value) { char *string = NULL; krb5_error_code retval; retval = pkinit_libdefault_string(context, realm, option, &string); if (retval == 0) { *ret_value = _krb5_conf_boolean(string); free(string); } else *ret_value = default_value; return 0; } krb5_error_code pkinit_libdefault_integer(krb5_context context, const krb5_data *realm, const char *option, int default_value, int *ret_value) { char *string = NULL; krb5_error_code retval; retval = pkinit_libdefault_string(context, realm, option, &string); if (retval == 0) { char *endptr; long l; l = strtol(string, &endptr, 0); if (endptr == string) *ret_value = default_value; else *ret_value = l; free(string); } return retval; } /* * COPYRIGHT (C) 2006,2007 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN * ALL RIGHTS RESERVED * * Permission is granted to use, copy, create derivative works * and redistribute this software and such derivative works * for any purpose, so long as the name of The University of * Michigan is not used in any advertising or publicity * pertaining to the use of distribution of this software * without specific, written prior authorization. If the * above copyright notice or any other identification of the * University of Michigan is included in any copy of any * portion of this software, then the disclaimer below must * also be included. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF * SUCH DAMAGES. */ #include #include #include #include #include "pkinit.h" static krb5_error_code pkinit_server_get_edata(krb5_context context, krb5_kdc_req * request, struct _krb5_db_entry_new * client, struct _krb5_db_entry_new * server, preauth_get_entry_data_proc server_get_entry_data, void *pa_plugin_context, krb5_pa_data * data); static krb5_error_code pkinit_server_verify_padata(krb5_context context, struct _krb5_db_entry_new * client, krb5_data *req_pkt, krb5_kdc_req * request, krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data * data, preauth_get_entry_data_proc server_get_entry_data, void *pa_plugin_context, void **pa_request_context, krb5_data **e_data, krb5_authdata ***authz_data); static krb5_error_code pkinit_server_return_padata(krb5_context context, krb5_pa_data * padata, struct _krb5_db_entry_new * client, krb5_data *req_pkt, krb5_kdc_req * request, krb5_kdc_rep * reply, struct _krb5_key_data * client_key, krb5_keyblock * encrypting_key, krb5_pa_data ** send_pa, preauth_get_entry_data_proc server_get_entry_data, void *pa_plugin_context, void **pa_request_context); static int pkinit_server_get_flags (krb5_context kcontext, krb5_preauthtype patype); static krb5_error_code pkinit_init_kdc_req_context (krb5_context, void **blob); static void pkinit_fini_kdc_req_context (krb5_context context, void *blob); static int pkinit_server_plugin_init_realm (krb5_context context, const char *realmname, pkinit_kdc_context *pplgctx); static void pkinit_server_plugin_fini_realm (krb5_context context, pkinit_kdc_context plgctx); static int pkinit_server_plugin_init (krb5_context context, void **blob, const char **realmnames); static void pkinit_server_plugin_fini (krb5_context context, void *blob); static pkinit_kdc_context pkinit_find_realm_context (krb5_context context, void *pa_plugin_context, krb5_principal princ); static krb5_error_code pkinit_create_edata(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, pkinit_plg_opts *opts, krb5_error_code err_code, krb5_data **e_data) { krb5_error_code retval = KRB5KRB_ERR_GENERIC; pkiDebug("pkinit_create_edata: creating edata for error %d (%s)\n", err_code, error_message(err_code)); switch(err_code) { case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE: retval = pkinit_create_td_trusted_certifiers(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data); break; case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED: retval = pkinit_create_td_dh_parameters(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, opts, e_data); break; case KRB5KDC_ERR_INVALID_CERTIFICATE: case KRB5KDC_ERR_REVOKED_CERTIFICATE: retval = pkinit_create_td_invalid_certificate(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data); break; default: pkiDebug("no edata needed for error %d (%s)\n", err_code, error_message(err_code)); retval = 0; goto cleanup; } cleanup: return retval; } /* ARGSUSED */ static krb5_error_code pkinit_server_get_edata(krb5_context context, krb5_kdc_req * request, struct _krb5_db_entry_new * client, struct _krb5_db_entry_new * server, preauth_get_entry_data_proc server_get_entry_data, void *pa_plugin_context, krb5_pa_data * data) { krb5_error_code retval = 0; pkinit_kdc_context plgctx = NULL; pkiDebug("pkinit_server_get_edata: entered!\n"); /* * If we don't have a realm context for the given realm, * don't tell the client that we support pkinit! */ plgctx = pkinit_find_realm_context(context, pa_plugin_context, request->server); if (plgctx == NULL) retval = EINVAL; return retval; } static krb5_error_code verify_client_san(krb5_context context, pkinit_kdc_context plgctx, pkinit_kdc_req_context reqctx, krb5_principal client, int *valid_san) { krb5_error_code retval; krb5_principal *princs = NULL; krb5_principal *upns = NULL; int i; #ifdef DEBUG_SAN_INFO char *client_string = NULL, *san_string; #endif retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, &princs, plgctx->opts->allow_upn ? &upns : NULL, NULL); if (retval) { pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__); retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; goto out; } /* XXX Verify this is consistent with client side XXX */ #if 0 retval = call_san_checking_plugins(context, plgctx, reqctx, princs, upns, NULL, &plugin_decision, &ignore); pkiDebug("%s: call_san_checking_plugins() returned retval %d\n", __FUNCTION__); if (retval) { retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; goto cleanup; } pkiDebug("%s: call_san_checking_plugins() returned decision %d\n", __FUNCTION__, plugin_decision); if (plugin_decision != NO_DECISION) { retval = plugin_decision; goto out; } #endif #ifdef DEBUG_SAN_INFO krb5_unparse_name(context, client, &client_string); #endif pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__); for (i = 0; princs != NULL && princs[i] != NULL; i++) { #ifdef DEBUG_SAN_INFO krb5_unparse_name(context, princs[i], &san_string); pkiDebug("%s: Comparing client '%s' to pkinit san value '%s'\n", __FUNCTION__, client_string, san_string); krb5_free_unparsed_name(context, san_string); #endif if (krb5_principal_compare(context, princs[i], client)) { pkiDebug("%s: pkinit san match found\n", __FUNCTION__); *valid_san = 1; retval = 0; goto out; } } pkiDebug("%s: no pkinit san match found\n", __FUNCTION__); /* * XXX if cert has names but none match, should we * be returning KRB5KDC_ERR_CLIENT_NAME_MISMATCH here? */ if (upns == NULL) { pkiDebug("%s: no upn sans (or we wouldn't accept them anyway)\n", __FUNCTION__); retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; goto out; } pkiDebug("%s: Checking upn sans\n", __FUNCTION__); for (i = 0; upns[i] != NULL; i++) { #ifdef DEBUG_SAN_INFO krb5_unparse_name(context, upns[i], &san_string); pkiDebug("%s: Comparing client '%s' to upn san value '%s'\n", __FUNCTION__, client_string, san_string); krb5_free_unparsed_name(context, san_string); #endif if (krb5_principal_compare(context, upns[i], client)) { pkiDebug("%s: upn san match found\n", __FUNCTION__); *valid_san = 1; retval = 0; goto out; } } pkiDebug("%s: no upn san match found\n", __FUNCTION__); /* We found no match */ if (princs != NULL || upns != NULL) { *valid_san = 0; /* XXX ??? If there was one or more name in the cert, but * none matched the client name, then return mismatch? */ retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; } retval = 0; out: if (princs != NULL) { for (i = 0; princs[i] != NULL; i++) krb5_free_principal(context, princs[i]); free(princs); } if (upns != NULL) { for (i = 0; upns[i] != NULL; i++) krb5_free_principal(context, upns[i]); free(upns); } #ifdef DEBUG_SAN_INFO if (client_string != NULL) krb5_free_unparsed_name(context, client_string); #endif pkiDebug("%s: returning retval %d, valid_san %d\n", __FUNCTION__, retval, *valid_san); return retval; } static krb5_error_code verify_client_eku(krb5_context context, pkinit_kdc_context plgctx, pkinit_kdc_req_context reqctx, int *eku_accepted) { krb5_error_code retval; *eku_accepted = 0; if (plgctx->opts->require_eku == 0) { pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__); *eku_accepted = 1; retval = 0; goto out; } retval = crypto_check_cert_eku(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, 0, /* kdc cert */ plgctx->opts->accept_secondary_eku, eku_accepted); if (retval) { pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n", __FUNCTION__, retval, error_message(retval)); goto out; } out: pkiDebug("%s: returning retval %d, eku_accepted %d\n", __FUNCTION__, retval, *eku_accepted); return retval; } /* ARGSUSED */ static krb5_error_code pkinit_server_verify_padata(krb5_context context, struct _krb5_db_entry_new * client, krb5_data *req_pkt, krb5_kdc_req * request, krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data * data, preauth_get_entry_data_proc server_get_entry_data, void *pa_plugin_context, void **pa_request_context, krb5_data **e_data, krb5_authdata ***authz_data) { krb5_error_code retval = 0; krb5_octet_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL}; krb5_data *encoded_pkinit_authz_data = NULL; krb5_pa_pk_as_req *reqp = NULL; krb5_pa_pk_as_req_draft9 *reqp9 = NULL; krb5_auth_pack *auth_pack = NULL; krb5_auth_pack_draft9 *auth_pack9 = NULL; pkinit_kdc_context plgctx = NULL; pkinit_kdc_req_context reqctx; /* Solaris Kerberos: set but not used */ #if 0 krb5_preauthtype pa_type; #endif krb5_checksum cksum = {0, 0, 0, NULL}; krb5_data *der_req = NULL; int valid_eku = 0, valid_san = 0; krb5_authdata **my_authz_data = NULL, *pkinit_authz_data = NULL; krb5_kdc_req *tmp_as_req = NULL; krb5_data k5data; pkiDebug("pkinit_verify_padata: entered!\n"); /* Solaris Kerberos */ if (data == NULL || data->length == 0 || data->contents == NULL) return 0; if (pa_plugin_context == NULL || e_data == NULL) return EINVAL; plgctx = pkinit_find_realm_context(context, pa_plugin_context, request->server); if (plgctx == NULL) return 0; #ifdef DEBUG_ASN1 print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req"); #endif /* create a per-request context */ retval = pkinit_init_kdc_req_context(context, (void **)&reqctx); if (retval) goto cleanup; reqctx->pa_type = data->pa_type; PADATA_TO_KRB5DATA(data, &k5data); switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n"); retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp); if (retval) { pkiDebug("decode_krb5_pa_pk_as_req failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin(reqp->signedAuthPack.data, reqp->signedAuthPack.length, "/tmp/kdc_signed_data"); #endif retval = cms_signeddata_verify(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT, plgctx->opts->require_crl_checking, reqp->signedAuthPack.data, reqp->signedAuthPack.length, &authp_data.data, &authp_data.length, &krb5_authz.data, &krb5_authz.length); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n"); retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9); if (retval) { pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin(reqp9->signedAuthPack.data, reqp9->signedAuthPack.length, "/tmp/kdc_signed_data_draft9"); #endif retval = cms_signeddata_verify(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, plgctx->opts->require_crl_checking, reqp9->signedAuthPack.data, reqp9->signedAuthPack.length, &authp_data.data, &authp_data.length, &krb5_authz.data, &krb5_authz.length); break; default: pkiDebug("unrecognized pa_type = %d\n", data->pa_type); retval = EINVAL; goto cleanup; } if (retval) { pkiDebug("pkcs7_signeddata_verify failed\n"); goto cleanup; } retval = verify_client_san(context, plgctx, reqctx, request->client, &valid_san); if (retval) goto cleanup; if (!valid_san) { pkiDebug("%s: did not find an acceptable SAN in user certificate\n", __FUNCTION__); retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; goto cleanup; } retval = verify_client_eku(context, plgctx, reqctx, &valid_eku); if (retval) goto cleanup; if (!valid_eku) { pkiDebug("%s: did not find an acceptable EKU in user certificate\n", __FUNCTION__); retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack"); #endif OCTETDATA_TO_KRB5DATA(&authp_data, &k5data); switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack); if (retval) { pkiDebug("failed to decode krb5_auth_pack\n"); goto cleanup; } /* check dh parameters */ if (auth_pack->clientPublicValue != NULL) { retval = server_check_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, &auth_pack->clientPublicValue->algorithm.parameters, plgctx->opts->dh_min_bits); if (retval) { pkiDebug("bad dh parameters\n"); goto cleanup; } } /* * The KDC may have modified the request after decoding it. * We need to compute the checksum on the data that * came from the client. Therefore, we use the original * packet contents. */ retval = k5int_decode_krb5_as_req(req_pkt, &tmp_as_req); if (retval) { pkiDebug("decode_krb5_as_req returned %d\n", (int)retval); goto cleanup; } retval = k5int_encode_krb5_kdc_req_body(tmp_as_req, &der_req); if (retval) { pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval); goto cleanup; } retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 0, der_req, &cksum); if (retval) { pkiDebug("unable to calculate AS REQ checksum\n"); goto cleanup; } if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length || memcmp(cksum.contents, auth_pack->pkAuthenticator.paChecksum.contents, cksum.length)) { pkiDebug("failed to match the checksum\n"); #ifdef DEBUG_CKSUM pkiDebug("calculating checksum on buf size (%d)\n", req_pkt->length); print_buffer(req_pkt->data, req_pkt->length); pkiDebug("received checksum type=%d size=%d ", auth_pack->pkAuthenticator.paChecksum.checksum_type, auth_pack->pkAuthenticator.paChecksum.length); print_buffer(auth_pack->pkAuthenticator.paChecksum.contents, auth_pack->pkAuthenticator.paChecksum.length); pkiDebug("expected checksum type=%d size=%d ", cksum.checksum_type, cksum.length); print_buffer(cksum.contents, cksum.length); #endif retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED; goto cleanup; } /* check if kdcPkId present and match KDC's subjectIdentifier */ if (reqp->kdcPkId.data != NULL) { int valid_kdcPkId = 0; retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, reqp->kdcPkId.data, reqp->kdcPkId.length, &valid_kdcPkId); if (retval) goto cleanup; if (!valid_kdcPkId) pkiDebug("kdcPkId in AS_REQ does not match KDC's cert" "RFC says to ignore and proceed\n"); } /* remember the decoded auth_pack for verify_padata routine */ reqctx->rcv_auth_pack = auth_pack; auth_pack = NULL; break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9); if (retval) { pkiDebug("failed to decode krb5_auth_pack_draft9\n"); goto cleanup; } if (auth_pack9->clientPublicValue != NULL) { retval = server_check_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, &auth_pack9->clientPublicValue->algorithm.parameters, plgctx->opts->dh_min_bits); if (retval) { pkiDebug("bad dh parameters\n"); goto cleanup; } } /* remember the decoded auth_pack for verify_padata routine */ reqctx->rcv_auth_pack9 = auth_pack9; auth_pack9 = NULL; break; } /* return authorization data to be included in the ticket */ switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: my_authz_data = malloc(2 * sizeof(*my_authz_data)); if (my_authz_data == NULL) { retval = ENOMEM; pkiDebug("Couldn't allocate krb5_authdata ptr array\n"); goto cleanup; } my_authz_data[1] = NULL; my_authz_data[0] = malloc(sizeof(krb5_authdata)); if (my_authz_data[0] == NULL) { retval = ENOMEM; pkiDebug("Couldn't allocate krb5_authdata\n"); free(my_authz_data); goto cleanup; } /* AD-INITIAL-VERIFIED-CAS must be wrapped in AD-IF-RELEVANT */ my_authz_data[0]->magic = KV5M_AUTHDATA; my_authz_data[0]->ad_type = KRB5_AUTHDATA_IF_RELEVANT; /* create an internal AD-INITIAL-VERIFIED-CAS data */ pkinit_authz_data = malloc(sizeof(krb5_authdata)); if (pkinit_authz_data == NULL) { retval = ENOMEM; pkiDebug("Couldn't allocate krb5_authdata\n"); free(my_authz_data[0]); free(my_authz_data); goto cleanup; } pkinit_authz_data->ad_type = KRB5_AUTHDATA_INITIAL_VERIFIED_CAS; /* content of this ad-type contains the certification path with which the client certificate was validated */ pkinit_authz_data->contents = krb5_authz.data; pkinit_authz_data->length = krb5_authz.length; retval = k5int_encode_krb5_authdata_elt(pkinit_authz_data, &encoded_pkinit_authz_data); #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)encoded_pkinit_authz_data->data, encoded_pkinit_authz_data->length, "/tmp/kdc_pkinit_authz_data"); #endif free(pkinit_authz_data); if (retval) { pkiDebug("k5int_encode_krb5_authdata_elt failed\n"); free(my_authz_data[0]); free(my_authz_data); goto cleanup; } my_authz_data[0]->contents = (krb5_octet *) encoded_pkinit_authz_data->data; my_authz_data[0]->length = encoded_pkinit_authz_data->length; *authz_data = my_authz_data; pkiDebug("Returning %d bytes of authorization data\n", krb5_authz.length); encoded_pkinit_authz_data->data = NULL; /* Don't free during cleanup*/ free(encoded_pkinit_authz_data); break; default: *authz_data = NULL; } /* remember to set the PREAUTH flag in the reply */ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; *pa_request_context = reqctx; reqctx = NULL; cleanup: if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) { pkiDebug("pkinit_verify_padata failed: creating e-data\n"); if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, plgctx->opts, retval, e_data)) pkiDebug("pkinit_create_edata failed\n"); } switch ((int)data->pa_type) { case KRB5_PADATA_PK_AS_REQ: free_krb5_pa_pk_as_req(&reqp); if (cksum.contents != NULL) free(cksum.contents); if (der_req != NULL) krb5_free_data(context, der_req); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: free_krb5_pa_pk_as_req_draft9(&reqp9); } if (tmp_as_req != NULL) k5int_krb5_free_kdc_req(context, tmp_as_req); if (authp_data.data != NULL) free(authp_data.data); if (krb5_authz.data != NULL) free(krb5_authz.data); if (reqctx != NULL) pkinit_fini_kdc_req_context(context, reqctx); if (auth_pack != NULL) free_krb5_auth_pack(&auth_pack); if (auth_pack9 != NULL) free_krb5_auth_pack_draft9(context, &auth_pack9); return retval; } /* ARGSUSED */ static krb5_error_code pkinit_server_return_padata(krb5_context context, krb5_pa_data * padata, struct _krb5_db_entry_new * client, krb5_data *req_pkt, krb5_kdc_req * request, krb5_kdc_rep * reply, struct _krb5_key_data * client_key, krb5_keyblock * encrypting_key, krb5_pa_data ** send_pa, preauth_get_entry_data_proc server_get_entry_data, void *pa_plugin_context, void **pa_request_context) { krb5_error_code retval = 0; krb5_data scratch = {0, 0, NULL}; krb5_pa_pk_as_req *reqp = NULL; krb5_pa_pk_as_req_draft9 *reqp9 = NULL; int i = 0; unsigned char *subjectPublicKey = NULL; unsigned char *dh_pubkey = NULL, *server_key = NULL; unsigned int subjectPublicKey_len = 0; unsigned int server_key_len = 0, dh_pubkey_len = 0; krb5_kdc_dh_key_info dhkey_info; krb5_data *encoded_dhkey_info = NULL; krb5_pa_pk_as_rep *rep = NULL; krb5_pa_pk_as_rep_draft9 *rep9 = NULL; krb5_data *out_data = NULL; krb5_enctype enctype = -1; krb5_reply_key_pack *key_pack = NULL; krb5_reply_key_pack_draft9 *key_pack9 = NULL; krb5_data *encoded_key_pack = NULL; unsigned int num_types; krb5_cksumtype *cksum_types = NULL; pkinit_kdc_context plgctx; pkinit_kdc_req_context reqctx; int fixed_keypack = 0; *send_pa = NULL; /* Solaris Kerberos */ if (padata == NULL || padata->length == 0 || padata->contents == NULL) return 0; if (pa_request_context == NULL || *pa_request_context == NULL) { pkiDebug("missing request context \n"); return EINVAL; } plgctx = pkinit_find_realm_context(context, pa_plugin_context, request->server); if (plgctx == NULL) { pkiDebug("Unable to locate correct realm context\n"); return ENOENT; } pkiDebug("pkinit_return_padata: entered!\n"); reqctx = (pkinit_kdc_req_context)*pa_request_context; if (encrypting_key->contents) { free(encrypting_key->contents); encrypting_key->length = 0; encrypting_key->contents = NULL; } for(i = 0; i < request->nktypes; i++) { enctype = request->ktype[i]; if (!krb5_c_valid_enctype(enctype)) continue; else { pkiDebug("KDC picked etype = %d\n", enctype); break; } } if (i == request->nktypes) { retval = KRB5KDC_ERR_ETYPE_NOSUPP; goto cleanup; } switch((int)reqctx->pa_type) { case KRB5_PADATA_PK_AS_REQ: init_krb5_pa_pk_as_rep(&rep); if (rep == NULL) { retval = ENOMEM; goto cleanup; } /* let's assume it's RSA. we'll reset it to DH if needed */ rep->choice = choice_pa_pk_as_rep_encKeyPack; break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: init_krb5_pa_pk_as_rep_draft9(&rep9); if (rep9 == NULL) { retval = ENOMEM; goto cleanup; } rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack; break; default: retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } if (reqctx->rcv_auth_pack != NULL && reqctx->rcv_auth_pack->clientPublicValue != NULL) { subjectPublicKey = reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.data; subjectPublicKey_len = reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.length; rep->choice = choice_pa_pk_as_rep_dhInfo; } else if (reqctx->rcv_auth_pack9 != NULL && reqctx->rcv_auth_pack9->clientPublicValue != NULL) { subjectPublicKey = reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.data; subjectPublicKey_len = reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.length; rep9->choice = choice_pa_pk_as_rep_draft9_dhSignedData; } /* if this DH, then process finish computing DH key */ if (((rep != NULL) && (rep->choice == choice_pa_pk_as_rep_dhInfo)) || ((rep9 != NULL) && rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData)) { pkiDebug("received DH key delivery AS REQ\n"); retval = server_process_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, subjectPublicKey, subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len, &server_key, &server_key_len); if (retval) { pkiDebug("failed to process/create dh paramters\n"); goto cleanup; } } if ((rep9 != NULL && rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) || (rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) { retval = pkinit_octetstring2key(context, enctype, server_key, server_key_len, encrypting_key); if (retval) { pkiDebug("pkinit_octetstring2key failed: %s\n", error_message(retval)); goto cleanup; } dhkey_info.subjectPublicKey.length = dh_pubkey_len; dhkey_info.subjectPublicKey.data = dh_pubkey; dhkey_info.nonce = request->nonce; dhkey_info.dhKeyExpiration = 0; retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info, &encoded_dhkey_info); if (retval) { pkiDebug("encode_krb5_kdc_dh_key_info failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)encoded_dhkey_info->data, encoded_dhkey_info->length, "/tmp/kdc_dh_key_info"); #endif switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: retval = cms_signeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1, (unsigned char *)encoded_dhkey_info->data, encoded_dhkey_info->length, &rep->u.dh_Info.dhSignedData.data, &rep->u.dh_Info.dhSignedData.length); if (retval) { pkiDebug("failed to create pkcs7 signed data\n"); goto cleanup; } break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: retval = cms_signeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1, (unsigned char *)encoded_dhkey_info->data, encoded_dhkey_info->length, &rep9->u.dhSignedData.data, &rep9->u.dhSignedData.length); if (retval) { pkiDebug("failed to create pkcs7 signed data\n"); goto cleanup; } break; } } else { pkiDebug("received RSA key delivery AS REQ\n"); retval = krb5_c_make_random_key(context, enctype, encrypting_key); if (retval) { pkiDebug("unable to make a session key\n"); goto cleanup; } /* check if PA_TYPE of 132 is present which means the client is * requesting that a checksum is send back instead of the nonce */ for (i = 0; request->padata[i] != NULL; i++) { pkiDebug("%s: Checking pa_type 0x%08x\n", __FUNCTION__, request->padata[i]->pa_type); if (request->padata[i]->pa_type == 132) fixed_keypack = 1; } pkiDebug("%s: return checksum instead of nonce = %d\n", __FUNCTION__, fixed_keypack); /* if this is an RFC reply or draft9 client requested a checksum * in the reply instead of the nonce, create an RFC-style keypack */ if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) { init_krb5_reply_key_pack(&key_pack); if (key_pack == NULL) { retval = ENOMEM; goto cleanup; } /* retrieve checksums for a given enctype of the reply key */ retval = krb5_c_keyed_checksum_types(context, encrypting_key->enctype, &num_types, &cksum_types); if (retval) goto cleanup; /* pick the first of acceptable enctypes for the checksum */ retval = krb5_c_make_checksum(context, cksum_types[0], encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, req_pkt, &key_pack->asChecksum); if (retval) { pkiDebug("unable to calculate AS REQ checksum\n"); goto cleanup; } #ifdef DEBUG_CKSUM pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length); print_buffer(req_pkt->data, req_pkt->length); pkiDebug("checksum size = %d\n", key_pack->asChecksum.length); print_buffer(key_pack->asChecksum.contents, key_pack->asChecksum.length); pkiDebug("encrypting key (%d)\n", encrypting_key->length); print_buffer(encrypting_key->contents, encrypting_key->length); #endif krb5_copy_keyblock_contents(context, encrypting_key, &key_pack->replyKey); retval = k5int_encode_krb5_reply_key_pack(key_pack, &encoded_key_pack); if (retval) { pkiDebug("failed to encode reply_key_pack\n"); goto cleanup; } } switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: rep->choice = choice_pa_pk_as_rep_encKeyPack; retval = cms_envelopeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, (unsigned char *)encoded_key_pack->data, encoded_key_pack->length, &rep->u.encKeyPack.data, &rep->u.encKeyPack.length); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: /* if the request is from the broken draft9 client that * expects back a nonce, create it now */ if (!fixed_keypack) { init_krb5_reply_key_pack_draft9(&key_pack9); if (key_pack9 == NULL) { retval = ENOMEM; goto cleanup; } key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce; krb5_copy_keyblock_contents(context, encrypting_key, &key_pack9->replyKey); retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9, &encoded_key_pack); if (retval) { pkiDebug("failed to encode reply_key_pack\n"); goto cleanup; } } rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack; retval = cms_envelopeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, (unsigned char *)encoded_key_pack->data, encoded_key_pack->length, &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length); break; } if (retval) { pkiDebug("failed to create pkcs7 enveloped data: %s\n", error_message(retval)); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)encoded_key_pack->data, encoded_key_pack->length, "/tmp/kdc_key_pack"); switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: print_buffer_bin(rep->u.encKeyPack.data, rep->u.encKeyPack.length, "/tmp/kdc_enc_key_pack"); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: print_buffer_bin(rep9->u.encKeyPack.data, rep9->u.encKeyPack.length, "/tmp/kdc_enc_key_pack"); break; } #endif } switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data); break; } if (retval) { pkiDebug("failed to encode AS_REP\n"); goto cleanup; } #ifdef DEBUG_ASN1 if (out_data != NULL) print_buffer_bin((unsigned char *)out_data->data, out_data->length, "/tmp/kdc_as_rep"); #endif *send_pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data)); if (*send_pa == NULL) { retval = ENOMEM; free(out_data->data); free(out_data); out_data = NULL; goto cleanup; } (*send_pa)->magic = KV5M_PA_DATA; switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP; break; case KRB5_PADATA_PK_AS_REQ_OLD: case KRB5_PADATA_PK_AS_REP_OLD: (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD; break; } (*send_pa)->length = out_data->length; (*send_pa)->contents = (krb5_octet *) out_data->data; cleanup: pkinit_fini_kdc_req_context(context, reqctx); if (scratch.data != NULL) free(scratch.data); if (out_data != NULL) free(out_data); if (encoded_dhkey_info != NULL) krb5_free_data(context, encoded_dhkey_info); if (encoded_key_pack != NULL) krb5_free_data(context, encoded_key_pack); if (dh_pubkey != NULL) free(dh_pubkey); if (server_key != NULL) free(server_key); if (cksum_types != NULL) free(cksum_types); switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: free_krb5_pa_pk_as_req(&reqp); free_krb5_pa_pk_as_rep(&rep); free_krb5_reply_key_pack(&key_pack); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: free_krb5_pa_pk_as_req_draft9(&reqp9); free_krb5_pa_pk_as_rep_draft9(&rep9); if (!fixed_keypack) free_krb5_reply_key_pack_draft9(&key_pack9); else free_krb5_reply_key_pack(&key_pack); break; } if (retval) pkiDebug("pkinit_verify_padata failure"); return retval; } /* ARGSUSED */ static int pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype) { return PA_SUFFICIENT | PA_REPLACES_KEY; } static krb5_preauthtype supported_server_pa_types[] = { KRB5_PADATA_PK_AS_REQ, KRB5_PADATA_PK_AS_REQ_OLD, KRB5_PADATA_PK_AS_REP_OLD, 0 }; /* ARGSUSED */ static void pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx) { /* * There is nothing currently allocated by pkinit_init_kdc_profile() * which needs to be freed here. */ } static krb5_error_code pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx) { krb5_error_code retval; char *eku_string = NULL; pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname); retval = pkinit_kdcdefault_string(context, plgctx->realmname, "pkinit_identity", &plgctx->idopts->identity); if (retval != 0 || NULL == plgctx->idopts->identity) { retval = EINVAL; krb5_set_error_message(context, retval, "No pkinit_identity supplied for realm %s", plgctx->realmname); goto errout; } retval = pkinit_kdcdefault_strings(context, plgctx->realmname, "pkinit_anchors", &plgctx->idopts->anchors); if (retval != 0 || NULL == plgctx->idopts->anchors) { retval = EINVAL; krb5_set_error_message(context, retval, "No pkinit_anchors supplied for realm %s", plgctx->realmname); goto errout; } /* Solaris Kerberos */ (void) pkinit_kdcdefault_strings(context, plgctx->realmname, "pkinit_pool", &plgctx->idopts->intermediates); (void) pkinit_kdcdefault_strings(context, plgctx->realmname, "pkinit_revoke", &plgctx->idopts->crls); (void) pkinit_kdcdefault_string(context, plgctx->realmname, "pkinit_kdc_ocsp", &plgctx->idopts->ocsp); (void) pkinit_kdcdefault_string(context, plgctx->realmname, "pkinit_mappings_file", &plgctx->idopts->dn_mapping_file); (void) pkinit_kdcdefault_integer(context, plgctx->realmname, "pkinit_dh_min_bits", PKINIT_DEFAULT_DH_MIN_BITS, &plgctx->opts->dh_min_bits); if (plgctx->opts->dh_min_bits < 1024) { pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, " "using default value (%d) instead\n", __FUNCTION__, plgctx->opts->dh_min_bits, PKINIT_DEFAULT_DH_MIN_BITS); plgctx->opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS; } (void) pkinit_kdcdefault_boolean(context, plgctx->realmname, "pkinit_allow_upn", 0, &plgctx->opts->allow_upn); (void) pkinit_kdcdefault_boolean(context, plgctx->realmname, "pkinit_require_crl_checking", 0, &plgctx->opts->require_crl_checking); (void) pkinit_kdcdefault_string(context, plgctx->realmname, "pkinit_eku_checking", &eku_string); if (eku_string != NULL) { if (strcasecmp(eku_string, "kpClientAuth") == 0) { plgctx->opts->require_eku = 1; plgctx->opts->accept_secondary_eku = 0; } else if (strcasecmp(eku_string, "scLogin") == 0) { plgctx->opts->require_eku = 1; plgctx->opts->accept_secondary_eku = 1; } else if (strcasecmp(eku_string, "none") == 0) { plgctx->opts->require_eku = 0; plgctx->opts->accept_secondary_eku = 0; } else { pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n", __FUNCTION__, eku_string); } free(eku_string); } return 0; errout: pkinit_fini_kdc_profile(context, plgctx); return retval; } /* ARGSUSED */ static pkinit_kdc_context pkinit_find_realm_context(krb5_context context, void *pa_plugin_context, krb5_principal princ) { int i; pkinit_kdc_context *realm_contexts = pa_plugin_context; if (pa_plugin_context == NULL) return NULL; for (i = 0; realm_contexts[i] != NULL; i++) { pkinit_kdc_context p = realm_contexts[i]; if ((p->realmname_len == princ->realm.length) && (strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) { pkiDebug("%s: returning context at %p for realm '%s'\n", __FUNCTION__, p, p->realmname); return p; } } pkiDebug("%s: unable to find realm context for realm '%.*s'\n", __FUNCTION__, princ->realm.length, princ->realm.data); return NULL; } static int pkinit_server_plugin_init_realm(krb5_context context, const char *realmname, pkinit_kdc_context *pplgctx) { krb5_error_code retval = ENOMEM; pkinit_kdc_context plgctx = NULL; *pplgctx = NULL; plgctx = (pkinit_kdc_context) calloc(1, sizeof(*plgctx)); if (plgctx == NULL) goto errout; pkiDebug("%s: initializing context at %p for realm '%s'\n", __FUNCTION__, plgctx, realmname); (void) memset(plgctx, 0, sizeof(*plgctx)); plgctx->magic = PKINIT_CTX_MAGIC; plgctx->realmname = strdup(realmname); if (plgctx->realmname == NULL) goto errout; plgctx->realmname_len = strlen(plgctx->realmname); retval = pkinit_init_plg_crypto(&plgctx->cryptoctx); if (retval) goto errout; retval = pkinit_init_plg_opts(&plgctx->opts); if (retval) goto errout; retval = pkinit_init_identity_crypto(&plgctx->idctx); if (retval) goto errout; retval = pkinit_init_identity_opts(&plgctx->idopts); if (retval) goto errout; retval = pkinit_init_kdc_profile(context, plgctx); if (retval) goto errout; /* * Solaris Kerberos: * Some methods of storing key information (PKCS11, PKCS12,...) may * require interactive prompting. */ retval = pkinit_identity_set_prompter(plgctx->idctx, krb5_prompter_posix, NULL); if (retval) goto errout; retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL, plgctx->idopts, plgctx->idctx, 0, NULL); if (retval) goto errout; pkiDebug("%s: returning context at %p for realm '%s'\n", __FUNCTION__, plgctx, realmname); *pplgctx = plgctx; retval = 0; errout: if (retval) pkinit_server_plugin_fini_realm(context, plgctx); return retval; } static int pkinit_server_plugin_init(krb5_context context, void **blob, const char **realmnames) { krb5_error_code retval = ENOMEM; pkinit_kdc_context plgctx, *realm_contexts = NULL; int i, j; size_t numrealms; retval = pkinit_accessor_init(); if (retval) return retval; /* Determine how many realms we may need to support */ for (i = 0; realmnames[i] != NULL; i++) {}; numrealms = i; realm_contexts = (pkinit_kdc_context *) calloc(numrealms+1, sizeof(pkinit_kdc_context)); if (realm_contexts == NULL) return ENOMEM; for (i = 0, j = 0; i < numrealms; i++) { pkiDebug("%s: processing realm '%s'\n", __FUNCTION__, realmnames[i]); retval = pkinit_server_plugin_init_realm(context, realmnames[i], &plgctx); if (retval == 0 && plgctx != NULL) realm_contexts[j++] = plgctx; } if (j == 0) { /* * Solaris Kerberos * Improve error messages for the common case of a single realm */ if (numrealms != 1) { retval = EINVAL; krb5_set_error_message(context, retval, "No realms configured " "correctly for pkinit support"); } goto errout; } *blob = realm_contexts; retval = 0; pkiDebug("%s: returning context at %p\n", __FUNCTION__, realm_contexts); errout: if (retval) pkinit_server_plugin_fini(context, realm_contexts); return retval; } static void pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx) { if (plgctx == NULL) return; pkinit_fini_kdc_profile(context, plgctx); pkinit_fini_identity_opts(plgctx->idopts); pkinit_fini_identity_crypto(plgctx->idctx); pkinit_fini_plg_crypto(plgctx->cryptoctx); pkinit_fini_plg_opts(plgctx->opts); free(plgctx->realmname); free(plgctx); } static void pkinit_server_plugin_fini(krb5_context context, void *blob) { pkinit_kdc_context *realm_contexts = blob; int i; if (realm_contexts == NULL) return; for (i = 0; realm_contexts[i] != NULL; i++) { pkinit_server_plugin_fini_realm(context, realm_contexts[i]); } pkiDebug("%s: freeing context at %p\n", __FUNCTION__, realm_contexts); free(realm_contexts); } static krb5_error_code pkinit_init_kdc_req_context(krb5_context context, void **ctx) { krb5_error_code retval = ENOMEM; pkinit_kdc_req_context reqctx = NULL; reqctx = (pkinit_kdc_req_context)malloc(sizeof(*reqctx)); if (reqctx == NULL) return retval; (void) memset(reqctx, 0, sizeof(*reqctx)); reqctx->magic = PKINIT_CTX_MAGIC; retval = pkinit_init_req_crypto(&reqctx->cryptoctx); if (retval) goto cleanup; reqctx->rcv_auth_pack = NULL; reqctx->rcv_auth_pack9 = NULL; pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx); *ctx = reqctx; retval = 0; cleanup: if (retval) pkinit_fini_kdc_req_context(context, reqctx); return retval; } static void pkinit_fini_kdc_req_context(krb5_context context, void *ctx) { pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx; if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) { pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx); return; } pkiDebug("%s: freeing reqctx at %p\n", __FUNCTION__, reqctx); pkinit_fini_req_crypto(reqctx->cryptoctx); if (reqctx->rcv_auth_pack != NULL) free_krb5_auth_pack(&reqctx->rcv_auth_pack); if (reqctx->rcv_auth_pack9 != NULL) free_krb5_auth_pack_draft9(context, &reqctx->rcv_auth_pack9); free(reqctx); } struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = { "pkinit", /* name */ supported_server_pa_types, /* pa_type_list */ pkinit_server_plugin_init, /* (*init_proc) */ pkinit_server_plugin_fini, /* (*fini_proc) */ pkinit_server_get_flags, /* (*flags_proc) */ pkinit_server_get_edata, /* (*edata_proc) */ pkinit_server_verify_padata,/* (*verify_proc) */ pkinit_server_return_padata,/* (*return_proc) */ NULL, /* (*freepa_reqcontext_proc) */ }; # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # include ../Makefile.lib # Hammerhead: amd64-only SUBDIRS= $(MACH64) all : TARGET= all clean : TARGET= clean clobber : TARGET= clobber install : TARGET= install LIBRARY= libss.a POFILE = $(LIBRARY:%.a=%.po) POFILES = generic.po .KEEP_STATE: all clean install: $(SUBDIRS) clobber: $(SUBDIRS) $(RM) $(CLOBBERFILES) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: _msg: $(MSGDOMAIN) .WAIT $(POFILE) $(RM) $(MSGDOMAIN)/$(POFILE) $(CP) $(POFILE) $(MSGDOMAIN) $(POFILE): $(DERIVED_FILES) .WAIT $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ generic.po: FRC $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext *.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po $(MSGDOMAIN): $(INS.dir) # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. LIBRARY= libss.a VERS= .1 SSOBJS= \ data.o \ error.o \ execute_cmd.o \ help.o \ invocation.o \ list_rqs.o \ listen.o \ pager.o \ parse.o \ prompt.o \ request_tbl.o \ requests.o \ std_rqs.o OBJECTS= $(SSOBJS) # include library definitions include ../../Makefile.lib SRCS= $(SSOBJS:%.o=../%.c) LIBS= $(DYNLIB) include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 #override liblink INS.liblink= -$(RM) $@; $(SYMLINK) $(LIBLINKS)$(VERS) $@ CPPFLAGS += -DHAVE_LIBSOCKET=1 -DHAVE_LIBNSL=1 -DHAS_STRDUP=1 \ -DUSE_DIRENT_H=1 -DWAIT_USES_INT=1 -DPOSIX_SIGNALS=1 \ -D_REENTRANT -DUSE_SIGPROCMASK=1 -DRETSIGTYPE=void \ -DHAVE_STDARG_H=1 -DHAVE_STDLIB_H=1 -DHAVE_COMPILE=1 \ -DHAVE_UNISTD_H=1 -DHAVE_UMASK=1 -DHAVE_SRAND48=1 \ -DHAVESRAND=1 -DHAVESRANDOM=1 -DHAVE_RE_COMP=1 \ -DHAVE_RE_EXEC=1 -DHAVE_REGCOMP=1 -DHAVE_REGEXEC=1 \ -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/lib/krb5 # Hammerhead: -I.. must be in CPPFLAGS (not CFLAGS) to survive Makefile.master.64 reset CFLAGS += $(CCVERBOSE) CPPFLAGS += -I.. CERRWARN += -Wno-switch CERRWARN += -Wno-unused-variable SMOFF += all_func_returns DYNFLAGS += $(KRUNPATH) $(KMECHLIB) $(ZIGNORE) LDLIBS += -lc -ltecla .KEEP_STATE: all: $(LIBS) # include library targets include ../../Makefile.targ # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2025 Hammerhead Project # include ../Makefile.com include $(SRC)/lib/Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) /* Copyright 1987, 1989 by the Student Information Processing Board of the Massachusetts Institute of Technology Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Furthermore if you modify this software you must label your software as modified software and not distribute it in such a fashion that it might be confused with the original M.I.T. software. M.I.T. and the M.I.T. S.I.P.B. make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. */ /* * Copyright (c) 2000 by Sun Microsystems, Inc. * All rights reserved. */ /* * Copyright 1987, 1988, 1989 Massachusetts Institute of Technology * (Student Information Processing Board) * * For copyright info, see copyright.h. */ #include #include "ss_internal.h" #include "copyright.h" static const char copyright[] = "Copyright 1987, 1988, 1989 by the Massachusetts Institute of Technology"; ss_data **_ss_table = (ss_data **)NULL; char *_ss_pager_name = (char *)NULL; /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 1987, 1988, 1989 by MIT Student Information Processing * Board * * For copyright information, see copyright.h. */ #include #include "copyright.h" #include "com_err.h" #include "ss_internal.h" char * ss_name(sci_idx) int sci_idx; { register char *ret_val; register ss_data *infop; infop = ss_info(sci_idx); if (infop->current_request == (char const *)NULL) { ret_val = malloc((unsigned) (strlen(infop->subsystem_name)+1) * sizeof(char)); if (ret_val == (char *)NULL) return((char *)NULL); strcpy(ret_val, infop->subsystem_name); return(ret_val); } else { register char *cp; register char const *cp1; ret_val = malloc((unsigned)sizeof(char) * (strlen(infop->subsystem_name)+ strlen(infop->current_request)+ 4)); cp = ret_val; cp1 = infop->subsystem_name; while (*cp1) *cp++ = *cp1++; *cp++ = ' '; *cp++ = '('; cp1 = infop->current_request; while (*cp1) *cp++ = *cp1++; *cp++ = ')'; *cp = '\0'; return(ret_val); } } void ss_error (int sci_idx, long code, const char * fmt, ...) { register char *whoami; va_list pvar; va_start (pvar, fmt); whoami = ss_name (sci_idx); com_err_va (whoami, code, fmt, pvar); free (whoami); va_end(pvar); } void ss_perror (sci_idx, code, msg) /* for compatibility */ int sci_idx; long code; char const *msg; { ss_error (sci_idx, code, "%s", msg); } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 1987, 1988, 1989 by Massachusetts Institute of Technology * * For copyright info, see copyright.h. */ #include "ss_internal.h" #include "copyright.h" #include /* * get_request(tbl, idx) * * Function: * Gets the idx'th request from the request table pointed to * by tbl. * Arguments: * tbl (ss_request_table *) * pointer to request table * idx (int) * index into table * Returns: * (ss_request_entry *) * pointer to request table entry * Notes: * Has been replaced by a macro. */ #ifdef __SABER__ /* sigh. saber won't deal with pointer-to-const-struct */ static struct _ss_request_entry * get_request (tbl, idx) ss_request_table * tbl; int idx; { struct _ss_request_table *tbl1 = (struct _ss_request_table *) tbl; struct _ss_request_entry *e = (struct _ss_request_entry *) tbl1->requests; return e + idx; } #else #define get_request(tbl,idx) ((tbl) -> requests + (idx)) #endif /* * check_request_table(rqtbl, argc, argv, sci_idx) * * Function: * If the command string in argv[0] is in the request table, execute * the commands and return error code 0. Otherwise, return error * code ss_et_command_not_found. * Arguments: * rqtbl (ss_request_table *) * pointer to request table * argc (int) * number of elements in argv[] * argv (char *[]) * argument string array * sci_idx (int) * ss-internal index for subsystem control info structure * Returns: * (int) * zero if command found, ss_et_command_not_found otherwise * Notes: */ static int check_request_table (rqtbl, argc, argv, sci_idx) register ss_request_table *rqtbl; int argc; char *argv[]; int sci_idx; { #ifdef __SABER__ struct _ss_request_entry *request; #else register ss_request_entry *request; #endif register ss_data *info; register char const * const * name; char *string = argv[0]; int i; info = ss_info(sci_idx); info->argc = argc; info->argv = argv; for (i = 0; (request = get_request(rqtbl, i))->command_names; i++) { for (name = request->command_names; *name; name++) if (!strcmp(*name, string)) { info->current_request = request->command_names[0]; (request->function)(argc, (const char *const *) argv, sci_idx,info->info_ptr); info->current_request = (char *)NULL; return(0); } } return(SS_ET_COMMAND_NOT_FOUND); } /* * really_execute_command(sci_idx, argc, argv) * * Function: * Fills in the argc, argv values in the subsystem entry and * call the appropriate routine. * Arguments: * sci_idx (int) * ss-internal index for subsystem control info structure * argc (int) * number of arguments in argument list * argv (char **[]) * pointer to parsed argument list (may be reallocated * on abbrev expansion) * * Returns: * (int) * Zero if successful, ss_et_command_not_found otherwise. * Notes: */ static int really_execute_command (sci_idx, argc, argv) int sci_idx; int argc; char **argv[]; { register ss_request_table **rqtbl; register ss_data *info; info = ss_info(sci_idx); for (rqtbl = info->rqt_tables; *rqtbl; rqtbl++) { if (check_request_table (*rqtbl, argc, *argv, sci_idx) == 0) return(0); } return(SS_ET_COMMAND_NOT_FOUND); } /* * ss_execute_command(sci_idx, argv) * * Function: * Executes a parsed command list within the subsystem. * Arguments: * sci_idx (int) * ss-internal index for subsystem control info structure * argv (char *[]) * parsed argument list * Returns: * (int) * Zero if successful, ss_et_command_not_found otherwise. * Notes: */ int ss_execute_command(sci_idx, argv) int sci_idx; register char *argv[]; { register int i, argc; char **argp; argc = 0; for (argp = argv; *argp; argp++) argc++; argp = (char **)malloc((argc+1)*sizeof(char *)); for (i = 0; i <= argc; i++) argp[i] = argv[i]; i = really_execute_command(sci_idx, argc, &argp); free(argp); return(i); } /* * ss_execute_line(sci_idx, line_ptr) * * Function: * Parses and executes a command line within a subsystem. * Arguments: * sci_idx (int) * ss-internal index for subsystem control info structure * line_ptr (char *) * Pointer to command line to be parsed. * Returns: * (int) * Error code. * Notes: */ int ss_execute_line (sci_idx, line_ptr) int sci_idx; char *line_ptr; { char **argv; int argc, ret; /* flush leading whitespace */ while (line_ptr[0] == ' ' || line_ptr[0] == '\t') line_ptr++; /* check if it should be sent to operating system for execution */ if (*line_ptr == '!') { if (ss_info(sci_idx)->flags.escape_disabled) return SS_ET_ESCAPE_DISABLED; else { line_ptr++; system(line_ptr); return 0; } } /* parse it */ /* Solaris Kerberos */ (void) ss_parse(sci_idx, line_ptr, &argc, &argv, 0); if (argc == 0) return 0; /* look it up in the request tables, execute if found */ ret = really_execute_command (sci_idx, argc, &argv); free(argv); return(ret); } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 1987, 1988 by MIT Student Information Processing Board * * For copyright info, see copyright.h. */ #include #include #include #include #include /* just for O_* */ #include #include "ss_internal.h" #include "copyright.h" #include extern void ss_list_requests(); void ss_help (argc, argv, sci_idx, info_ptr) int argc; char const * const *argv; int sci_idx; pointer info_ptr; { char buffer[MAXPATHLEN]; char const *request_name; int code; int fd, child; register int idx; register ss_data *info; request_name = ss_current_request(sci_idx, &code); if (code != 0) { ss_perror(sci_idx, code, ""); return; /* no ss_abort_line, if invalid invocation */ } if (argc == 1) { ss_list_requests(argc, argv, sci_idx, info_ptr); return; } else if (argc != 2) { /* should do something better than this */ snprintf(buffer, sizeof (buffer), (char *)dgettext(TEXT_DOMAIN, "usage:\n\t%s [topic|command]\nor\t%s\n"), request_name, request_name); ss_perror(sci_idx, 0, buffer); return; } info = ss_info(sci_idx); if (info->info_dirs == (char **)NULL) { ss_perror(sci_idx, SS_ET_NO_INFO_DIR, (char *)NULL); return; } if (info->info_dirs[0] == (char *)NULL) { ss_perror(sci_idx, SS_ET_NO_INFO_DIR, (char *)NULL); return; } for (idx = 0; info->info_dirs[idx] != (char *)NULL; idx++) { (void) strncpy(buffer, info->info_dirs[idx], sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] = '\0'; (void) strncat(buffer, "/", sizeof(buffer) - 1 - strlen(buffer)); (void) strncat(buffer, argv[1], sizeof(buffer) - 1 - strlen(buffer)); (void) strncat(buffer, ".info", sizeof(buffer) - 1 - strlen(buffer)); if ((fd = open(&buffer[0], O_RDONLY)) >= 0) goto got_it; } if ((fd = open(&buffer[0], O_RDONLY)) < 0) { char buf[MAXPATHLEN]; strncpy(buf, "No info found for ", sizeof(buf) - 1); buf[sizeof(buf) - 1] = '\0'; strncat(buf, argv[1], sizeof(buf) - 1 - strlen(buf)); ss_perror(sci_idx, 0, buf); return; } got_it: switch (child = fork()) { case -1: ss_perror(sci_idx, errno, "Can't fork for pager"); return; case 0: (void) dup2(fd, 0); /* put file on stdin */ ss_page_stdin(); default: (void) close(fd); /* what can we do if it fails? */ #ifdef WAIT_USES_INT while (wait((int *)NULL) != child) { #else while (wait((union wait *)NULL) != child) { #endif /* do nothing if wrong pid */ }; } } #ifndef USE_DIRENT_H #include #else #include #endif void ss_add_info_dir(sci_idx, info_dir, code_ptr) int sci_idx; char *info_dir; int *code_ptr; { register ss_data *info; DIR *d; int n_dirs; register char **dirs; info = ss_info(sci_idx); if (info_dir == NULL && *info_dir) { *code_ptr = SS_ET_NO_INFO_DIR; return; } if ((d = opendir(info_dir)) == (DIR *)NULL) { *code_ptr = errno; return; } closedir(d); dirs = info->info_dirs; for (n_dirs = 0; dirs[n_dirs] != (char *)NULL; n_dirs++) ; /* get number of non-NULL dir entries */ dirs = (char **)realloc((char *)dirs, (unsigned)(n_dirs + 2)*sizeof(char *)); if (dirs == (char **)NULL) { info->info_dirs = (char **)NULL; *code_ptr = errno; return; } info->info_dirs = dirs; dirs[n_dirs + 1] = (char *)NULL; dirs[n_dirs] = malloc((unsigned)strlen(info_dir)+1); strcpy(dirs[n_dirs], info_dir); *code_ptr = 0; } void ss_delete_info_dir(sci_idx, info_dir, code_ptr) int sci_idx; char *info_dir; int *code_ptr; { register char **i_d; register char **info_dirs; info_dirs = ss_info(sci_idx)->info_dirs; for (i_d = info_dirs; *i_d; i_d++) { if (!strcmp(*i_d, info_dir)) { while (*i_d) { *i_d = *(i_d+1); i_d++; } *code_ptr = 0; return; } } *code_ptr = SS_ET_NO_INFO_DIR; } /* * Copyright 1987, 1988 by MIT Student Information Processing Board * * For copyright information, see copyright.h. */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "ss_internal.h" #include "copyright.h" #define size sizeof(ss_data *) int ss_create_invocation(subsystem_name, version_string, info_ptr, request_table_ptr, code_ptr) char *subsystem_name, *version_string; char *info_ptr; ss_request_table *request_table_ptr; int *code_ptr; { register int sci_idx; register ss_data *new_table; register ss_data **table; *code_ptr = 0; table = _ss_table; new_table = (ss_data *) malloc(sizeof(ss_data)); if (table == (ss_data **) NULL) { table = (ss_data **) malloc(2 * size); table[0] = table[1] = (ss_data *)NULL; } for (sci_idx = 1; table[sci_idx] != (ss_data *)NULL; sci_idx++) ; table = (ss_data **) realloc((char *)table, ((unsigned)sci_idx+2)*size); table[sci_idx+1] = (ss_data *) NULL; table[sci_idx] = new_table; new_table->subsystem_name = subsystem_name; new_table->subsystem_version = version_string; new_table->argv = (char **)NULL; new_table->current_request = (char *)NULL; new_table->info_dirs = (char **)malloc(sizeof(char *)); *new_table->info_dirs = (char *)NULL; new_table->info_ptr = info_ptr; /* Solaris Kerberos */ new_table->prompt = malloc((unsigned)strlen(subsystem_name)+3); strcpy(new_table->prompt, subsystem_name); strcat(new_table->prompt, ": "); #ifdef silly new_table->abbrev_info = ss_abbrev_initialize("/etc/passwd", code_ptr); #else new_table->abbrev_info = NULL; #endif new_table->flags.escape_disabled = 0; new_table->flags.abbrevs_disabled = 0; new_table->rqt_tables = (ss_request_table **) calloc(2, sizeof(ss_request_table *)); *(new_table->rqt_tables) = request_table_ptr; *(new_table->rqt_tables+1) = (ss_request_table *) NULL; _ss_table = table; return(sci_idx); } void ss_delete_invocation(sci_idx) int sci_idx; { register ss_data *t; int ignored_code; t = ss_info(sci_idx); free(t->prompt); free(t->rqt_tables); while(t->info_dirs[0] != (char *)NULL) ss_delete_info_dir(sci_idx, t->info_dirs[0], &ignored_code); free((char *)t->info_dirs); free((char *)t); } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 1987, 1988 by MIT Student Information Processing Board * * For copyright information, see copyright.h. */ #include "copyright.h" #include "ss_internal.h" #include #include #include #include #ifdef lint /* "lint returns a value which is sometimes ignored" */ #define DONT_USE(x) x=x; #else /* !lint */ #define DONT_USE(x) ; #endif /* lint */ extern int ss_pager_create(); static char const twentyfive_spaces[26] = " "; static char const NL[2] = "\n"; void ss_list_requests(argc, argv, sci_idx, info_ptr) int argc; const char * const *argv; int sci_idx; #ifdef __STDC__ void *info_ptr; #else char *info_ptr; #endif { register ss_request_entry *entry; register char const * const *name; register int spacing; register ss_request_table **table; char buffer[BUFSIZ]; FILE *output; int fd; #ifdef POSIX_SIGNALS struct sigaction nsig, osig; sigset_t nmask, omask; #else int mask; RETSIGTYPE (*func)(); #endif #ifndef WAIT_USES_INT union wait waitb; #else int waitb; #endif DONT_USE(argc); DONT_USE(argv); #ifdef POSIX_SIGNALS sigemptyset(&nmask); sigaddset(&nmask, SIGINT); sigprocmask(SIG_BLOCK, &nmask, &omask); nsig.sa_handler = SIG_IGN; sigemptyset(&nsig.sa_mask); nsig.sa_flags = 0; sigaction(SIGINT, &nsig, &osig); #else mask = sigblock(sigmask(SIGINT)); func = signal(SIGINT, SIG_IGN); #endif fd = ss_pager_create(); output = fdopen(fd, "w"); #ifdef POSIX_SIGNALS sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0); #else sigsetmask(mask); #endif fprintf (output, dgettext(TEXT_DOMAIN, "Available %s requests:\n\n"), ss_info (sci_idx) -> subsystem_name); for (table = ss_info(sci_idx)->rqt_tables; *table; table++) { entry = (*table)->requests; for (; entry->command_names; entry++) { spacing = -2; buffer[0] = '\0'; if (entry->flags & SS_OPT_DONT_LIST) continue; buffer[sizeof(buffer) - 1] = '\0'; for (name = entry->command_names; *name; name++) { register int len = strlen(*name); strncat(buffer, *name, sizeof(buffer) - 1 - strlen(buffer)); spacing += len + 2; if (name[1]) { strncat(buffer, ", ", sizeof(buffer) - 1 - strlen(buffer)); } } if (spacing > 23) { strncat(buffer, NL, sizeof(buffer) - 1 - strlen(buffer)); fputs(buffer, output); spacing = 0; buffer[0] = '\0'; } strncat(buffer, twentyfive_spaces, strlen(twentyfive_spaces) - spacing); /* * Due to libss not knowing what TEXT_DOMAIN * the calling application is using for its info_string * messages, we know require the callers (ktutil,kadmin) * to L10N the messages before calling libss. */ strncat(buffer, entry->info_string, sizeof(buffer) -1 - strlen(buffer)); strncat(buffer, NL, sizeof(buffer) - 1 - strlen(buffer)); fputs(buffer, output); } } fclose(output); #ifndef NO_FORK wait(&waitb); #endif #ifdef POSIX_SIGNALS sigaction(SIGINT, &osig, (struct sigaction *)0); #else (void) signal(SIGINT, func); #endif } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Listener loop for subsystem library libss.a. * * util/ss/listen.c * * Copyright 1987, 1988 by MIT Student Information Processing Board * * For copyright information, see copyright.h. */ #include "copyright.h" #include "ss_internal.h" #include #include #include #include #include #include /* Solaris Kerberos */ #include #define MAX_LINE_LEN BUFSIZ #define MAX_HIST_LEN 8192 static ss_data *current_info; static jmp_buf listen_jmpb; static RETSIGTYPE print_prompt() { struct termios termbuf; if (tcgetattr(STDIN_FILENO, &termbuf) == 0) { termbuf.c_lflag |= ICANON|ISIG|ECHO; tcsetattr(STDIN_FILENO, TCSANOW, &termbuf); } (void) fputs(current_info->prompt, stdout); (void) fflush(stdout); } static RETSIGTYPE listen_int_handler(signo) int signo; { putc('\n', stdout); longjmp(listen_jmpb, 1); } /* Solaris Kerberos */ typedef struct _ss_commands { int sci_idx; const char **cmd; unsigned int count; } ss_commands; /* * Solaris Kerberos * get_commands fills out a ss_commands structure with pointers * to the top-level commands (char*) that a program supports. * count reflects the number of commands cmd holds. Memory must * be allocated by the caller. */ void get_commands(ss_commands *commands) { const char * const *cmd; ss_request_table **table; ss_request_entry *request; ss_data *info; commands->count = 0; info = ss_info(commands->sci_idx); for (table = info->rqt_tables; *table; table++) { for (request = (*table)->requests; request->command_names != NULL; request++) { for (cmd = request->command_names; cmd != NULL && *cmd != NULL; cmd++) { if (commands->cmd != NULL) commands->cmd[commands->count] = *cmd; commands->count++; } } } } /* * Solaris Kerberos * Match function used by libtecla for tab-completion. */ CPL_MATCH_FN(cmdmatch) { int argc, len, ws, i; char **argv, *l; ss_commands *commands = data; int ret = 0; /* Dup the line as ss_parse will modify the string */ l = strdup(line); if (l == NULL) return (ret); /* Tab-completion may happen in the middle of a line */ if (word_end != strlen(l)) l[word_end] = '\0'; if (ss_parse(commands->sci_idx, l, &argc, &argv, 1)) { free (l); return (ret); } /* Don't bother if the arg count is not 1 or 0 */ if (argc < 2) { len = argc ? strlen(argv[0]) : 0; ws = word_end - len; for (i = 0; i < commands->count; i++) { if (strncmp(commands->cmd[i], line + ws, len) == 0) { ret = cpl_add_completion(cpl, line, ws, word_end, commands->cmd[i] + len, "", " "); if (ret) break; } } } free(argv); free(l); return (ret); } int ss_listen (sci_idx) int sci_idx; { register char *cp; register ss_data *info; char buffer[BUFSIZ]; char *volatile end = buffer; int code; /* Solaris Kerberos */ char *input; GetLine *gl; GlReturnStatus ret; ss_commands commands; jmp_buf old_jmpb; ss_data *old_info = current_info; #ifdef POSIX_SIGNALS struct sigaction isig, csig, nsig, osig; sigset_t nmask, omask; #else register RETSIGTYPE (*sig_cont)(); RETSIGTYPE (*sig_int)(), (*old_sig_cont)(); int mask; #endif current_info = info = ss_info(sci_idx); info->abort = 0; /* Solaris Kerberos */ gl = new_GetLine(MAX_LINE_LEN, MAX_HIST_LEN); if (gl == NULL) { ss_error(sci_idx, 0, dgettext(TEXT_DOMAIN, "new_GetLine() failed.\n")); current_info = old_info; return (SS_ET_TECLA_ERR); } commands.sci_idx = sci_idx; commands.cmd = NULL; /* Find out how many commands there are */ get_commands(&commands); /* Alloc space for them */ commands.cmd = malloc(sizeof (char *) * commands.count); if (commands.cmd == NULL) { current_info = old_info; gl = del_GetLine(gl); return (ENOMEM); } /* Fill-in commands.cmd */ get_commands(&commands); if (gl_customize_completion(gl, &commands, cmdmatch) != 0 ) { ss_error(sci_idx, 0, dgettext(TEXT_DOMAIN, "failed to register completion function.\n")); free(commands.cmd); current_info = old_info; gl = del_GetLine(gl); return (SS_ET_TECLA_ERR); } #ifdef POSIX_SIGNALS csig.sa_handler = (RETSIGTYPE (*)())0; sigemptyset(&nmask); sigaddset(&nmask, SIGINT); sigprocmask(SIG_BLOCK, &nmask, &omask); #else sig_cont = (RETSIGTYPE (*)())0; mask = sigblock(sigmask(SIGINT)); #endif memcpy(old_jmpb, listen_jmpb, sizeof(jmp_buf)); #ifdef POSIX_SIGNALS nsig.sa_handler = listen_int_handler; sigemptyset(&nsig.sa_mask); nsig.sa_flags = 0; sigaction(SIGINT, &nsig, &isig); #else sig_int = signal(SIGINT, listen_int_handler); #endif setjmp(listen_jmpb); #ifdef POSIX_SIGNALS sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0); #else (void) sigsetmask(mask); #endif /* * Solaris Kerberos: * Let libtecla deal with SIGINT when it's doing its own processing * otherwise the input line won't be cleared on SIGINT. */ if (gl_trap_signal(gl, SIGINT, GLS_DONT_FORWARD, GLS_ABORT, 0)) { ss_error(sci_idx, 0, dgettext(TEXT_DOMAIN, "Failed to trap SIGINT.\n")); code = SS_ET_TECLA_ERR; goto egress; } while(!info->abort) { print_prompt(); *end = '\0'; #ifdef POSIX_SIGNALS nsig.sa_handler = listen_int_handler; /* fgets is not signal-safe */ osig = csig; sigaction(SIGCONT, &nsig, &csig); if ((RETSIGTYPE (*)())csig.sa_handler==(RETSIGTYPE (*)())listen_int_handler) csig = osig; #else old_sig_cont = sig_cont; sig_cont = signal(SIGCONT, print_prompt); if (sig_cont == print_prompt) sig_cont = old_sig_cont; #endif /* Solaris Kerberos */ input = gl_get_line(gl, info->prompt, NULL, -1); ret = gl_return_status(gl); switch (ret) { case (GLR_SIGNAL): gl_abandon_line(gl); continue; case (GLR_EOF): info->abort = 1; continue; case (GLR_ERROR): ss_error(sci_idx, 0, dgettext(TEXT_DOMAIN, "Failed to read line: %s\n"), gl_error_message(gl, NULL, 0)); info->abort = 1; code = SS_ET_TECLA_ERR; goto egress; } cp = strchr(input, '\n'); if (cp) { *cp = '\0'; if (cp == input) continue; } #ifdef POSIX_SIGNALS sigaction(SIGCONT, &csig, (struct sigaction *)0); #else (void) signal(SIGCONT, sig_cont); #endif for (end = input; *end; end++) ; code = ss_execute_line (sci_idx, input); if (code == SS_ET_COMMAND_NOT_FOUND) { register char *c = input; while (*c == ' ' || *c == '\t') c++; cp = strchr (c, ' '); if (cp) *cp = '\0'; cp = strchr (c, '\t'); if (cp) *cp = '\0'; ss_error (sci_idx, 0, dgettext(TEXT_DOMAIN, "Unknown request \"%s\". Type \"?\" for a request list."), c); } } code = 0; egress: /* Solaris Kerberos */ free(commands.cmd); gl = del_GetLine(gl); #ifdef POSIX_SIGNALS sigaction(SIGINT, &isig, (struct sigaction *)0); #else (void) signal(SIGINT, sig_int); #endif memcpy(listen_jmpb, old_jmpb, sizeof(jmp_buf)); current_info = old_info; return code; } void ss_abort_subsystem(sci_idx, code) int sci_idx; int code; { ss_info(sci_idx)->abort = 1; ss_info(sci_idx)->exit_status = code; } void ss_quit(argc, argv, sci_idx, infop) int argc; char const * const *argv; int sci_idx; pointer infop; { ss_abort_subsystem(sci_idx, 0); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION SUNWprivate_1.1 { global: ss_abort_subsystem; ss_add_info_dir; ss_add_request_table; ss_create_invocation; ss_delete_info_dir; ss_delete_invocation; ss_delete_request_table; ss_error; ss_execute_command; ss_execute_line; ss_get_prompt; ss_help; ss_listen; ss_list_requests; ss_name; ss_pager_create; ss_page_stdin; ss_parse; ss_perror; ss_quit; ss_self_identify; ss_set_prompt; ss_std_requests { ASSERT = { TYPE = OBJECT; SIZE = 8; }; }; ss_subsystem_name; ss_subsystem_version; ss_unimplemented; local: *; }; /* Copyright 1987 by the Student Information Processing Board of the Massachusetts Institute of Technology Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Furthermore if you modify this software you must label your software as modified software and not distribute it in such a fashion that it might be confused with the original M.I.T. software. M.I.T. and the M.I.T. S.I.P.B. make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * make_commands.c * * util/ss/mk_cmds.c * * Copyright 1987, 1988 by MIT Student Information Processing Board * * For copyright information, see copyright.h. */ #include "copyright.h" #include #include #include #include #include #include "ss_internal.h" static const char copyright[] = "Copyright 1987 by MIT Student Information Processing Board"; extern pointer malloc (unsigned); extern char *last_token; extern FILE *output_file; extern FILE *yyin, *yyout; #ifndef NO_YYLINENO extern int yylineno; #endif int main(argc, argv) int argc; char **argv; { char c_file[MAXPATHLEN]; int result; char *path, *p, *q; if (argc != 2) { fputs("Usage: ", stderr); fputs(argv[0], stderr); fputs("cmdtbl.ct\n", stderr); exit(1); } path = malloc(strlen(argv[1])+4); /* extra space to add ".ct" */ strcpy(path, argv[1]); p = strrchr(path, '/'); if (p == (char *)NULL) p = path; else p++; p = strrchr(p, '.'); if (p == (char *)NULL || strcmp(p, ".ct")) strcat(path, ".ct"); yyin = fopen(path, "rF"); if (!yyin) { perror(path); exit(1); } p = strrchr(path, '.'); *p = '\0'; q = rindex(path, '/'); strncpy(c_file, (q) ? q + 1 : path, sizeof(c_file) - 1); c_file[sizeof(c_file) - 1] = '\0'; strncat(c_file, ".c", sizeof(c_file) - 1 - strlen(c_file)); *p = '.'; output_file = fopen(c_file, "w+F"); if (!output_file) { perror(c_file); exit(1); } fputs("/* ", output_file); fputs(c_file, output_file); fputs(" - automatically generated from ", output_file); fputs(path, output_file); fputs(" */\n", output_file); fputs("#include \n\n", output_file); fputs("#ifndef __STDC__\n#define const\n#endif\n\n", output_file); /* parse it */ result = yyparse(); /* put file descriptors back where they belong */ fclose(yyin); /* bye bye input file */ fclose(output_file); /* bye bye output file */ return result; } yyerror(s) char *s; { fputs(s, stderr); #ifdef NO_YYLINENO fprintf(stderr, "\nLast token was '%s'\n", last_token); #else fprintf(stderr, "\nLine %d; last token was '%s'\n", yylineno, last_token); #endif } /* * Copyright 1987, 1988 by MIT Student Information Processing Board * * For copyright information, see copyright.h. */ #include "copyright.h" #include #include struct option { char *text; long value; }; static struct option options[] = { { "dont_list", SS_OPT_DONT_LIST }, { "^list", SS_OPT_DONT_LIST }, { "dont_summarize", SS_OPT_DONT_SUMMARIZE }, { "^summarize", SS_OPT_DONT_SUMMARIZE }, { (char *)NULL, 0 } }; long flag_val(string) register char *string; { register struct option *opt; for (opt = options; opt->text; opt++) if (!strcmp(opt->text, string)) return(opt->value); return(0); } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Pager: Routines to create a "more" running out of a particular file * descriptor. * * Copyright 1987, 1988 by MIT Student Information Processing Board * * For copyright information, see copyright.h. */ #include "ss_internal.h" #include "copyright.h" #include #include #include #include #include static char MORE[] = "more"; extern char *_ss_pager_name; extern char *getenv(); /* * this needs a *lot* of work.... * * run in same process * handle SIGINT sensibly * allow finer control -- put-page-break-here */ #ifndef NO_FORK int ss_pager_create(void) { int filedes[2]; if (pipe(filedes) != 0) return(-1); switch((int) fork()) { case -1: return(-1); case 0: /* * Child; dup read half to 0, close all but 0, 1, and 2 */ if (dup2(filedes[0], 0) == -1) exit(1); ss_page_stdin(); default: /* * Parent: close "read" side of pipe, return * "write" side. */ (void) close(filedes[0]); return(filedes[1]); } } #else /* don't fork */ int ss_pager_create(void) { int fd; fd = open("/dev/tty", O_WRONLY, 0); return fd; } #endif void ss_page_stdin(void) { int i; #ifdef POSIX_SIGNALS struct sigaction sa; sigset_t mask; #endif /* * Close all fd's, excepting stdin/stdout/stderr */ closefrom(3); #ifdef POSIX_SIGNALS sa.sa_handler = SIG_DFL; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGINT, &sa, NULL); #else (void) signal(SIGINT, SIG_DFL); #endif { #ifdef POSIX_SIGNALS sigemptyset(&mask); sigaddset(&mask, SIGINT); sigprocmask(SIG_UNBLOCK, &mask, NULL); #else int mask = sigblock(0); mask &= ~sigmask(SIGINT); sigsetmask(mask); #endif } if (_ss_pager_name == NULL) { if ((_ss_pager_name = getenv("PAGER")) == NULL) _ss_pager_name = MORE; } (void) execlp(_ss_pager_name, _ss_pager_name, NULL); { /* minimal recovery if pager program isn't found */ char buf[80]; register int n; while ((n = read(0, buf, 80)) > 0) write(1, buf, (unsigned) n); } exit(errno); } /* * Copyright 1987, 1988 by MIT Student Information Processing Board * * For copyright info, see copyright.h. */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "ss_internal.h" #include "copyright.h" #include enum parse_mode { WHITESPACE, TOKEN, QUOTED_STRING }; /* * Solaris Kerberos: * ss_parse has been modified slightly from the original in two ways. * 1) A new parameter "quiet" has been added which is used to silence * error or warning messages. * 2) ss_parse now returns an error status instead of argv - this is to * allow an error to be distinguished from no tokens when parsing an empty * string. * Both of these changes allow ss_parse to be used during tab-completion. */ /* * parse(line_ptr, argc_ptr) * * Function: * Parses line, dividing at whitespace, into tokens, returns * the "argc" and "argv" values. * Arguments: * line_ptr (char *) * Pointer to text string to be parsed. * argc_ptr (int *) * Where to put the "argc" (number of tokens) value. * argv_ptr (char ***) * Where to put the series of pointers to parsed tokens. * Returns: * error (0 - success, non-zero on failure) */ #define NEW_ARGV(old,n) (char **)realloc((char *)old,\ (unsigned)(n+2)*sizeof(char*)) int ss_parse (sci_idx, line_ptr, argc_ptr, argv_ptr, quiet) int sci_idx; register char *line_ptr; int *argc_ptr; char ***argv_ptr; int quiet; { register char **argv, *cp; register int argc; register enum parse_mode parse_mode; argv = (char **) malloc (sizeof(char *)); if (argv == (char **)NULL) { if (!quiet) ss_error(sci_idx, errno, "Can't allocate storage"); *argc_ptr = 0; *argv_ptr = argv; return(ENOMEM); } *argv = (char *)NULL; argc = 0; parse_mode = WHITESPACE; /* flushing whitespace */ cp = line_ptr; /* cp is for output */ while (1) { #ifdef DEBUG { printf ("character `%c', mode %d\n", *line_ptr, parse_mode); } #endif while (parse_mode == WHITESPACE) { if (*line_ptr == '\0') goto end_of_line; if (*line_ptr == ' ' || *line_ptr == '\t') { line_ptr++; continue; } if (*line_ptr == '"') { /* go to quoted-string mode */ parse_mode = QUOTED_STRING; cp = line_ptr++; argv = NEW_ARGV (argv, argc); argv[argc++] = cp; argv[argc] = NULL; } else { /* random-token mode */ parse_mode = TOKEN; cp = line_ptr; argv = NEW_ARGV (argv, argc); argv[argc++] = line_ptr; argv[argc] = NULL; } } while (parse_mode == TOKEN) { if (*line_ptr == '\0') { *cp++ = '\0'; goto end_of_line; } else if (*line_ptr == ' ' || *line_ptr == '\t') { *cp++ = '\0'; line_ptr++; parse_mode = WHITESPACE; } else if (*line_ptr == '"') { line_ptr++; parse_mode = QUOTED_STRING; } else { *cp++ = *line_ptr++; } } while (parse_mode == QUOTED_STRING) { if (*line_ptr == '\0') { if (!quiet) ss_error (sci_idx, 0, "Unbalanced quotes in command line"); free (argv); *argc_ptr = 0; *argv_ptr = NULL; return (-1); } else if (*line_ptr == '"') { if (*++line_ptr == '"') { *cp++ = '"'; line_ptr++; } else { parse_mode = TOKEN; } } else { *cp++ = *line_ptr++; } } } end_of_line: *argc_ptr = argc; #ifdef DEBUG { int i; printf ("argc = %d\n", argc); for (i = 0; i <= argc; i++) printf ("\targv[%2d] = `%s'\n", i, argv[i] ? argv[i] : ""); } #endif *argv_ptr = argv; return(0); } /* * Copyright (c) 2000 by Sun Microsystems, Inc. * All rights reserved. */ /* * prompt.c: Routines for retrieving and setting a prompt. * * util/ss/prompt.c * * Copyright 1987, 1988 by MIT Student Information Processing Board * * For copyright information, see copyright.h. */ #include "copyright.h" #include #include "ss_internal.h" void ss_set_prompt(sci_idx, new_prompt) int sci_idx; char *new_prompt; { ss_info(sci_idx)->prompt = new_prompt; } char * ss_get_prompt(sci_idx) int sci_idx; { return(ss_info(sci_idx)->prompt); } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 1987, 1988 by MIT Student Information Processing Board * * For copyright information, see copyright.h. */ #include "copyright.h" #include "ss_internal.h" #define ssrt ss_request_table /* for some readable code... */ void ss_add_request_table(sci_idx, rqtbl_ptr, position, code_ptr) int sci_idx; ssrt *rqtbl_ptr; int position; /* 1 -> becomes second... */ int *code_ptr; { register ss_data *info; register int i, size; info = ss_info(sci_idx); for (size=0; info->rqt_tables[size] != (ssrt *)NULL; size++) ; /* size == C subscript of NULL == #elements */ size += 2; /* new element, and NULL */ info->rqt_tables = (ssrt **)realloc(info->rqt_tables, size*sizeof(ssrt)); if (info->rqt_tables == (ssrt **)NULL) { *code_ptr = errno; return; } if (position > size - 2) position = size - 2; if (size > 1) for (i = size - 2; i >= position; i--) info->rqt_tables[i+1] = info->rqt_tables[i]; info->rqt_tables[position] = rqtbl_ptr; info->rqt_tables[size-1] = (ssrt *)NULL; *code_ptr = 0; } void ss_delete_request_table(sci_idx, rqtbl_ptr, code_ptr) int sci_idx; ssrt *rqtbl_ptr; int *code_ptr; { register ss_data *info; register ssrt **rt1, **rt2; *code_ptr = SS_ET_TABLE_NOT_FOUND; info = ss_info(sci_idx); rt1 = info->rqt_tables; for (rt2 = rt1; *rt1; rt1++) { if (*rt1 != rqtbl_ptr) { *rt2++ = *rt1; *code_ptr = 0; } } *rt2 = (ssrt *)NULL; return; } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Various minor routines... * * Copyright 1987, 1988, 1989 by MIT * * For copyright information, see mit-sipb-copyright.h. */ #include #include "ss_internal.h" #include #define DECLARE(name) void name(argc,argv,sci_idx,info_ptr)int argc,sci_idx;const char * const *argv; pointer info_ptr; /* * ss_self_identify -- assigned by default to the "." request */ DECLARE(ss_self_identify) { register ss_data *info = ss_info(sci_idx); printf((char *)dgettext(TEXT_DOMAIN, "%s version %s\n"), info->subsystem_name, info->subsystem_version); } /* * ss_subsystem_name -- print name of subsystem */ DECLARE(ss_subsystem_name) { printf("%s\n", ss_info(sci_idx)->subsystem_name); } /* * ss_subsystem_version -- print version of subsystem */ DECLARE(ss_subsystem_version) { printf("%s\n", ss_info(sci_idx)->subsystem_version); } /* * ss_unimplemented -- routine not implemented (should be * set up as (dont_list,dont_summarize)) */ DECLARE(ss_unimplemented) { ss_perror(sci_idx, SS_ET_UNIMPLEMENTED, ""); } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 1987, 1988 by MIT Student Information Processing Board * * For copyright information, see mit-sipb-copyright.h. */ #ifndef _ss_h #define _ss_h __FILE__ #include #include #ifdef __STDC__ #define __SS_CONST const #define __SS_PROTO (int, const char * const *, int, void *) #else #define __SS_CONST #define __SS_PROTO () #endif typedef struct _ss_request_entry { __SS_CONST char * __SS_CONST *command_names; /* whatever */ void (* __SS_CONST function) __SS_PROTO; /* foo */ char *info_string; /* Already L10ed cmd message */ int flags; /* 0 */ } ss_request_entry; typedef __SS_CONST struct _ss_request_table { int version; ss_request_entry *requests; } ss_request_table; #define SS_RQT_TBL_V2 2 typedef struct _ss_rp_options { /* DEFAULT VALUES */ int version; /* SS_RP_V1 */ void (*unknown) __SS_PROTO; /* call for unknown command */ int allow_suspend; int catch_int; } ss_rp_options; #define SS_RP_V1 1 #define SS_OPT_DONT_LIST 0x0001 #define SS_OPT_DONT_SUMMARIZE 0x0002 void ss_help __SS_PROTO; void ss_list_requests __SS_PROTO; void ss_quit __SS_PROTO; char *ss_current_request(); char *ss_name(int); void ss_error (int, long, char const *, ...); void ss_perror (int, long, char const *); int ss_listen (int); int ss_create_invocation(char *, char *, char *, ss_request_table *, int *); void ss_delete_invocation(int); void ss_add_info_dir(int , char *, int *); void ss_delete_info_dir(int , char *, int *); int ss_execute_command(int sci_idx, char **); void ss_abort_subsystem(int, int); void ss_set_prompt(int, char *); char *ss_get_prompt(int); void ss_add_request_table(int, ss_request_table *, int, int *); void ss_delete_request_table(int, ss_request_table *, int *); int ss_execute_line (int, char*); extern ss_request_table ss_std_requests; /* toggles the display of debugging messages */ void debugDisplaySS(int onOff); #endif /* _ss_h */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Solaris Kerberos: * This file is not generated automatically for Solaris Kerberos */ /* * ss_err.h: * This file is automatically generated; please do not edit it. */ #define SS_ET_SUBSYSTEM_ABORTED (748800L) #define SS_ET_VERSION_MISMATCH (748801L) #define SS_ET_NULL_INV (748802L) #define SS_ET_NO_INFO_DIR (748803L) #define SS_ET_COMMAND_NOT_FOUND (748804L) #define SS_ET_LINE_ABORTED (748805L) #define SS_ET_EOF (748806L) #define SS_ET_PERMISSION_DENIED (748807L) #define SS_ET_TABLE_NOT_FOUND (748808L) #define SS_ET_NO_HELP_FILE (748809L) #define SS_ET_ESCAPE_DISABLED (748810L) #define SS_ET_UNIMPLEMENTED (748811L) /* Solaris Kerberos */ #define SS_ET_TECLA_ERR (748812L) #define ERROR_TABLE_BASE_ss (748800L) /* for compatibility with older versions... */ #define ss_err_base ERROR_TABLE_BASE_ss /* * Copyright 1987, 1988 by MIT Student Information Processing Board * * For copyright information, see copyright.h. */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _ss_ss_internal_h #define _ss_ss_internal_h __FILE__ #include #include #include #ifdef HAVE_STDLIB_H #include #endif typedef void * pointer; #include #if defined(__GNUC__) #define LOCAL_ALLOC(x) __builtin_alloca(x) #define LOCAL_FREE(x) #else #if defined(vax) #define LOCAL_ALLOC(x) alloca(x) #define LOCAL_FREE(x) extern pointer alloca (unsigned); #else #if defined(__HIGHC__) /* Barf! */ pragma on(alloca); #define LOCAL_ALLOC(x) alloca(x) #define LOCAL_FREE(x) extern pointer alloca (unsigned); #else /* no alloca? */ #define LOCAL_ALLOC(x) malloc(x) #define LOCAL_FREE(x) free(x) #endif #endif #endif /* LOCAL_ALLOC stuff */ typedef char BOOL; typedef struct _ss_abbrev_entry { char *name; /* abbrev name */ char **abbrev; /* new tokens to insert */ unsigned int beginning_of_line : 1; } ss_abbrev_entry; typedef struct _ss_abbrev_list { int n_abbrevs; ss_abbrev_entry *first_abbrev; } ss_abbrev_list; typedef struct { /* char *path; */ ss_abbrev_list abbrevs[127]; } ss_abbrev_info; typedef struct _ss_data { /* init values */ /* this subsystem */ char *subsystem_name; char *subsystem_version; /* current request info */ int argc; char **argv; /* arg list */ char const *current_request; /* primary name */ /* info directory for 'help' */ char **info_dirs; /* to be extracted by subroutines */ pointer info_ptr; /* (void *) NULL */ /* for ss_listen processing */ char *prompt; ss_request_table **rqt_tables; ss_abbrev_info *abbrev_info; struct { unsigned int escape_disabled : 1, abbrevs_disabled : 1; } flags; /* to get out */ int abort; /* exit subsystem */ int exit_status; } ss_data; #define CURRENT_SS_VERSION 1 #define ss_info(sci_idx) (_ss_table[sci_idx]) #define ss_current_request(sci_idx,code_ptr) \ (*code_ptr=0,ss_info(sci_idx)->current_request) void ss_unknown_function(); void ss_delete_info_dir(); /* Solaris Kerberos */ int ss_parse (int, char *, int *, char ***, int); ss_abbrev_info *ss_abbrev_initialize (char *, int *); void ss_page_stdin (void) __NORETURN; int ss_pager_create (void); void ss_self_identify __SS_PROTO; void ss_subsystem_name __SS_PROTO; void ss_subsystem_version __SS_PROTO; void ss_unimplemented __SS_PROTO; extern ss_data **_ss_table; extern char *ss_et_msgs[]; #ifndef HAVE_STDLIB_H extern pointer malloc (unsigned); extern pointer realloc (pointer, unsigned); extern pointer calloc (unsigned, unsigned); #endif #if defined(USE_SIGPROCMASK) && !defined(POSIX_SIGNALS) /* fake sigmask, sigblock, sigsetmask */ #include #ifdef sigmask #undef sigmask #endif #define sigmask(x) (1L<<(x)-1) #define sigsetmask(x) sigprocmask(SIG_SETMASK,&x,NULL) static int _fake_sigstore; #define sigblock(x) (_fake_sigstore=x,sigprocmask(SIG_BLOCK,&_fake_sigstore,0)) #endif #endif /* _ss_internal_h */ /* std_rqs.c - automatically generated from std_rqs.ct */ #include #ifndef __STDC__ #define const #endif static char const * const ssu00001[] = { ".", (char const *)0 }; extern void ss_self_identify __SS_PROTO; static char const * const ssu00002[] = { "help", (char const *)0 }; extern void ss_help __SS_PROTO; static char const * const ssu00003[] = { "list_help", "lh", (char const *)0 }; extern void ss_unimplemented __SS_PROTO; static char const * const ssu00004[] = { "list_requests", "lr", "?", (char const *)0 }; extern void ss_list_requests __SS_PROTO; static char const * const ssu00005[] = { "quit", "q", (char const *)0 }; extern void ss_quit __SS_PROTO; static char const * const ssu00006[] = { "abbrev", "ab", (char const *)0 }; extern void ss_unimplemented __SS_PROTO; static char const * const ssu00007[] = { "execute", "e", (char const *)0 }; extern void ss_unimplemented __SS_PROTO; static char const * const ssu00008[] = { "?", (char const *)0 }; extern void ss_unimplemented __SS_PROTO; static char const * const ssu00009[] = { "subsystem_name", (char const *)0 }; extern void ss_subsystem_name __SS_PROTO; static char const * const ssu00010[] = { "subsystem_version", (char const *)0 }; extern void ss_subsystem_version __SS_PROTO; static ss_request_entry ssu00011[] = { { ssu00001, ss_self_identify, "Identify the subsystem.", 3 }, { ssu00002, ss_help, "Display info on command or topic.", 0 }, { ssu00003, ss_unimplemented, "List topics for which help is available.", 3 }, { ssu00004, ss_list_requests, "List available commands.", 0 }, { ssu00005, ss_quit, "Leave the subsystem.", 0 }, { ssu00006, ss_unimplemented, "Enable/disable abbreviation processing of request lines.", 3 }, { ssu00007, ss_unimplemented, "Execute a UNIX command line.", 3 }, { ssu00008, ss_unimplemented, "Produce a list of the most commonly used requests.", 3 }, { ssu00009, ss_subsystem_name, "Return the name of this subsystem.", 1 }, { ssu00010, ss_subsystem_version, "Return the version of this subsystem.", 1 }, { 0, 0, 0, 0 } }; ss_request_table ss_std_requests = { 2, ssu00011 }; /* * Copyright 1987, 1988 by MIT Student Information Processing Board * * For copyright information, see copyright.h. */ #include #include "copyright.h" #include "ss_internal.h" /* includes stdio and string */ extern FILE *output_file; char *gensym(), *str_concat3(), *quote(); extern long gensym_n; void write_ct(hdr, rql) char const *hdr, *rql; { char *sym; sym = gensym("ssu"); fputs("static ss_request_entry ", output_file); fputs(sym, output_file); fputs("[] = {\n", output_file); fputs(rql, output_file); fputs(" { 0, 0, 0, 0 }\n", output_file); fputs("};\n\nss_request_table ", output_file); fputs(hdr, output_file); fprintf(output_file, " = { %d, ", SS_RQT_TBL_V2); fputs(sym, output_file); fputs(" };\n", output_file); } char * generate_cmds_string(cmds) char const *cmds; { char * var_name = gensym("ssu"); fputs("static char const * const ", output_file); fputs(var_name, output_file); fputs("[] = {\n", output_file); fputs(cmds, output_file); fputs(",\n (char const *)0\n};\n", output_file); return(var_name); } void generate_function_definition(func) char const *func; { fputs("extern void ", output_file); fputs(func, output_file); fputs(" __SS_PROTO;\n", output_file); } char * generate_rqte(func_name, info_string, cmds, options) char const *func_name; char const *info_string; char const *cmds; int options; { int size; char *string, *var_name, numbuf[16]; var_name = generate_cmds_string(cmds); generate_function_definition(func_name); size = 6; /* " { " */ size += strlen(var_name)+8; /* "quux, " */ size += strlen(func_name)+8; /* "foo, " */ size += strlen(info_string)+8; /* "\"Info!\", " */ sprintf(numbuf, "%d", options); size += strlen(numbuf)+5; /* " }," + NL + NUL */ string = malloc(size); strcpy(string, " { "); strcat(string, var_name); strcat(string, ",\n "); strcat(string, func_name); strcat(string, ",\n "); strcat(string, info_string); strcat(string, ",\n "); strcat(string, numbuf); strcat(string, " },\n"); return(string); } char * gensym(name) char *name; { char *symbol; symbol = malloc((strlen(name)+6) * sizeof(char)); gensym_n++; sprintf(symbol, "%s%05ld", name, gensym_n); return(symbol); } /* concatenate three strings and return the result */ char *str_concat3(a, b, c) register char *a, *b, *c; { char *result; int size_a = strlen(a); int size_b = strlen(b); int size_c = strlen(c); result = malloc((size_a + size_b + size_c + 2)*sizeof(char)); strcpy(result, a); strcpy(&result[size_a], c); strcpy(&result[size_a+size_c], b); return(result); } /* return copy of string enclosed in double-quotes */ char *quote(string) register char *string; { register char *result; int len; len = strlen(string)+1; result = malloc(len+2); result[0] = '"'; strncpy(&result[1], string, len-1); result[len] = '"'; result[len+1] = '\0'; return(result); } #ifndef HAVE_STRDUP /* make duplicate of string and return pointer */ char *strdup(s) register char *s; { register int len = strlen(s) + 1; register char *new; new = malloc(len); strncpy(new, s, len); return(new); } #endif