1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* lib/kadm5/srv/pwqual_dict.c */ 3 /* 4 * Copyright (C) 2010 by the Massachusetts Institute of Technology. 5 * All rights reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 /* 27 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved 28 */ 29 30 /* Password quality module to look up passwords within the realm dictionary. */ 31 32 #include "k5-platform.h" 33 #include <krb5/pwqual_plugin.h> 34 #include <sys/types.h> 35 #include <sys/stat.h> 36 #include <unistd.h> 37 #include <kadm5/admin.h> 38 #include "adm_proto.h" 39 #include <syslog.h> 40 #include "server_internal.h" 41 42 typedef struct dict_moddata_st { 43 char **word_list; /* list of word pointers */ 44 char *word_block; /* actual word data */ 45 unsigned int word_count; /* number of words */ 46 } *dict_moddata; 47 48 49 /* 50 * Function: word_compare 51 * 52 * Purpose: compare two words in the dictionary. 53 * 54 * Arguments: 55 * w1 (input) pointer to first word 56 * w2 (input) pointer to second word 57 * <return value> result of strcmp 58 * 59 * Requires: 60 * w1 and w2 to point to valid memory 61 * 62 */ 63 64 static int 65 word_compare(const void *s1, const void *s2) 66 { 67 return (strcasecmp(*(const char **)s1, *(const char **)s2)); 68 } 69 70 /* 71 * Function: init-dict 72 * 73 * Purpose: Initialize in memory word dictionary 74 * 75 * Arguments: 76 * none 77 * <return value> KADM5_OK on success errno on failure; 78 * (but success on ENOENT) 79 * 80 * Requires: 81 * If WORDFILE exists, it must contain a list of words, 82 * one word per-line. 83 * 84 * Effects: 85 * If WORDFILE exists, it is read into memory sorted for future 86 * use. If it does not exist, it syslogs an error message and returns 87 * success. 88 * 89 * Modifies: 90 * word_list to point to a chunk of allocated memory containing 91 * pointers to words 92 * word_block to contain the dictionary. 93 * 94 */ 95 96 static int 97 init_dict(dict_moddata dict, const char *dict_file) 98 { 99 int fd; 100 size_t len, i; 101 char *p, *t; 102 struct stat sb; 103 104 if (dict_file == NULL) { 105 krb5_klog_syslog(LOG_INFO, 106 _("No dictionary file specified, continuing without " 107 "one.")); 108 return KADM5_OK; 109 } 110 if ((fd = open(dict_file, O_RDONLY)) == -1) { 111 if (errno == ENOENT) { 112 krb5_klog_syslog(LOG_ERR, 113 _("WARNING! Cannot find dictionary file %s, " 114 "continuing without one."), dict_file); 115 return KADM5_OK; 116 } else 117 return errno; 118 } 119 set_cloexec_fd(fd); 120 if (fstat(fd, &sb) == -1) { 121 close(fd); 122 return errno; 123 } 124 dict->word_block = malloc(sb.st_size + 1); 125 if (dict->word_block == NULL) { 126 (void)close(fd); 127 return ENOMEM; 128 } 129 if (read(fd, dict->word_block, sb.st_size) != sb.st_size) { 130 (void)close(fd); 131 return errno; 132 } 133 (void)close(fd); 134 dict->word_block[sb.st_size] = '\0'; 135 136 p = dict->word_block; 137 len = sb.st_size; 138 while(len > 0 && (t = memchr(p, '\n', len)) != NULL) { 139 *t = '\0'; 140 len -= t - p + 1; 141 p = t + 1; 142 dict->word_count++; 143 } 144 if ((dict->word_list = malloc(dict->word_count * sizeof(char *))) == NULL) 145 return ENOMEM; 146 p = dict->word_block; 147 for (i = 0; i < dict->word_count; i++) { 148 dict->word_list[i] = p; 149 p += strlen(p) + 1; 150 } 151 qsort(dict->word_list, dict->word_count, sizeof(char *), word_compare); 152 return KADM5_OK; 153 } 154 155 /* 156 * Function: destroy_dict 157 * 158 * Purpose: destroy in-core copy of dictionary. 159 * 160 * Arguments: 161 * none 162 * <return value> none 163 * Requires: 164 * nothing 165 * Effects: 166 * frees up memory occupied by word_list and word_block 167 * sets count back to 0, and resets the pointers to NULL 168 * 169 * Modifies: 170 * word_list, word_block, and word_count. 171 * 172 */ 173 174 static void 175 destroy_dict(dict_moddata dict) 176 { 177 if (dict == NULL) 178 return; 179 free(dict->word_list); 180 free(dict->word_block); 181 free(dict); 182 return; 183 } 184 185 /* Implement the password quality open method by reading in dict_file. */ 186 static krb5_error_code 187 dict_open(krb5_context context, const char *dict_file, 188 krb5_pwqual_moddata *data) 189 { 190 krb5_error_code ret; 191 dict_moddata dict; 192 193 *data = NULL; 194 195 /* Allocate and initialize a dictionary structure. */ 196 dict = malloc(sizeof(*dict)); 197 if (dict == NULL) 198 return ENOMEM; 199 dict->word_list = NULL; 200 dict->word_block = NULL; 201 dict->word_count = 0; 202 203 /* Fill in the dictionary structure with data from dict_file. */ 204 ret = init_dict(dict, dict_file); 205 if (ret != 0) { 206 destroy_dict(dict); 207 return ret; 208 } 209 210 *data = (krb5_pwqual_moddata)dict; 211 return 0; 212 } 213 214 /* Implement the password quality check method by checking the password 215 * against the dictionary, as well as against principal components. */ 216 static krb5_error_code 217 dict_check(krb5_context context, krb5_pwqual_moddata data, 218 const char *password, const char *policy_name, 219 krb5_principal princ, const char **languages) 220 { 221 dict_moddata dict = (dict_moddata)data; 222 223 /* Don't check the dictionary for principals with no password policy. */ 224 if (policy_name == NULL) 225 return 0; 226 227 /* Check against words in the dictionary if we successfully loaded one. */ 228 if (dict->word_list != NULL && 229 bsearch(&password, dict->word_list, dict->word_count, sizeof(char *), 230 word_compare) != NULL) 231 return KADM5_PASS_Q_DICT; 232 233 return 0; 234 } 235 236 /* Implement the password quality close method. */ 237 static void 238 dict_close(krb5_context context, krb5_pwqual_moddata data) 239 { 240 destroy_dict((dict_moddata)data); 241 } 242 243 krb5_error_code 244 pwqual_dict_initvt(krb5_context context, int maj_ver, int min_ver, 245 krb5_plugin_vtable vtable) 246 { 247 krb5_pwqual_vtable vt; 248 249 if (maj_ver != 1) 250 return KRB5_PLUGIN_VER_NOTSUPP; 251 vt = (krb5_pwqual_vtable)vtable; 252 vt->name = "dict"; 253 vt->open = dict_open; 254 vt->check = dict_check; 255 vt->close = dict_close; 256 return 0; 257 } 258