00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "includes.h"
00028 #include <keyutils.h>
00029
00030 #include "cifs_spnego.h"
00031
00032 const char *CIFSSPNEGO_VERSION = "1.2";
00033 static const char *prog = "cifs.upcall";
00034 typedef enum _secType {
00035 KRB5,
00036 MS_KRB5
00037 } secType_t;
00038 const DATA_BLOB data_blob_null = { NULL, 0, NULL };
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060 int handle_krb5_mech(const char *oid, const char *principal,
00061 DATA_BLOB * secblob, DATA_BLOB * sess_key)
00062 {
00063 int retval;
00064 DATA_BLOB tkt, tkt_wrapped;
00065
00066
00067 retval = cli_krb5_get_ticket(principal, 0,
00068 &tkt, sess_key, 0, NULL, NULL);
00069
00070 if (retval)
00071 return retval;
00072
00073
00074 tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
00075
00076
00077 *secblob = gen_negTokenInit(oid, tkt_wrapped);
00078
00079 data_blob_free(&tkt_wrapped);
00080 data_blob_free(&tkt);
00081 return retval;
00082 }
00083
00084 #define DKD_HAVE_HOSTNAME 1
00085 #define DKD_HAVE_VERSION 2
00086 #define DKD_HAVE_SEC 4
00087 #define DKD_HAVE_IPV4 8
00088 #define DKD_HAVE_IPV6 16
00089 #define DKD_HAVE_UID 32
00090 #define DKD_MUSTHAVE_SET (DKD_HAVE_HOSTNAME|DKD_HAVE_VERSION|DKD_HAVE_SEC)
00091
00092 int decode_key_description(const char *desc, int *ver, secType_t * sec,
00093 char **hostname, uid_t * uid)
00094 {
00095 int retval = 0;
00096 char *pos;
00097 const char *tkn = desc;
00098
00099 do {
00100 pos = index(tkn, ';');
00101 if (strncmp(tkn, "host=", 5) == 0) {
00102 int len;
00103
00104 if (pos == NULL) {
00105 len = strlen(tkn);
00106 } else {
00107 len = pos - tkn;
00108 }
00109 len -= 4;
00110 SAFE_FREE(*hostname);
00111 *hostname = SMB_XMALLOC_ARRAY(char, len);
00112 strlcpy(*hostname, tkn + 5, len);
00113 retval |= DKD_HAVE_HOSTNAME;
00114 } else if (strncmp(tkn, "ipv4=", 5) == 0) {
00115
00116 } else if (strncmp(tkn, "ipv6=", 5) == 0) {
00117
00118 } else if (strncmp(tkn, "sec=", 4) == 0) {
00119 if (strncmp(tkn + 4, "krb5", 4) == 0) {
00120 retval |= DKD_HAVE_SEC;
00121 *sec = KRB5;
00122 } else if (strncmp(tkn + 4, "mskrb5", 6) == 0) {
00123 retval |= DKD_HAVE_SEC;
00124 *sec = MS_KRB5;
00125 }
00126 } else if (strncmp(tkn, "uid=", 4) == 0) {
00127 errno = 0;
00128 *uid = strtol(tkn + 4, NULL, 16);
00129 if (errno != 0) {
00130 syslog(LOG_WARNING, "Invalid uid format: %s",
00131 strerror(errno));
00132 return 1;
00133 } else {
00134 retval |= DKD_HAVE_UID;
00135 }
00136 } else if (strncmp(tkn, "ver=", 4) == 0) {
00137 errno = 0;
00138 *ver = strtol(tkn + 4, NULL, 16);
00139 if (errno != 0) {
00140 syslog(LOG_WARNING,
00141 "Invalid version format: %s",
00142 strerror(errno));
00143 return 1;
00144 } else {
00145 retval |= DKD_HAVE_VERSION;
00146 }
00147 }
00148 if (pos == NULL)
00149 break;
00150 tkn = pos + 1;
00151 } while (tkn);
00152 return retval;
00153 }
00154
00155 int cifs_resolver(const key_serial_t key, const char *key_descr)
00156 {
00157 int c;
00158 struct addrinfo *addr;
00159 char ip[INET6_ADDRSTRLEN];
00160 void *p;
00161 const char *keyend = key_descr;
00162
00163 for (c = 1; c <= 4; c++) {
00164 keyend = index(keyend+1, ';');
00165 if (!keyend) {
00166 syslog(LOG_WARNING, "invalid key description: %s",
00167 key_descr);
00168 return 1;
00169 }
00170 }
00171 keyend++;
00172
00173
00174 c = getaddrinfo(keyend, NULL, NULL, &addr);
00175 if (c) {
00176 syslog(LOG_WARNING, "unable to resolve hostname: %s [%s]",
00177 keyend, gai_strerror(c));
00178 return 1;
00179 }
00180
00181
00182 if (addr->ai_family == AF_INET) {
00183 p = &(((struct sockaddr_in *)addr->ai_addr)->sin_addr);
00184 } else {
00185 p = &(((struct sockaddr_in6 *)addr->ai_addr)->sin6_addr);
00186 }
00187 if (!inet_ntop(addr->ai_family, p, ip, sizeof(ip))) {
00188 syslog(LOG_WARNING, "%s: inet_ntop: %s",
00189 __FUNCTION__, strerror(errno));
00190 freeaddrinfo(addr);
00191 return 1;
00192 }
00193
00194
00195 c = keyctl_instantiate(key, ip, strlen(ip)+1, 0);
00196 if (c == -1) {
00197 syslog(LOG_WARNING, "%s: keyctl_instantiate: %s",
00198 __FUNCTION__, strerror(errno));
00199 freeaddrinfo(addr);
00200 return 1;
00201 }
00202
00203 freeaddrinfo(addr);
00204 return 0;
00205 }
00206
00207 void
00208 usage(void)
00209 {
00210 syslog(LOG_WARNING, "Usage: %s [-c] [-v] key_serial", prog);
00211 fprintf(stderr, "Usage: %s [-c] [-v] key_serial\n", prog);
00212 }
00213
00214 int main(const int argc, char *const argv[])
00215 {
00216 struct cifs_spnego_msg *keydata = NULL;
00217 DATA_BLOB secblob = data_blob_null;
00218 DATA_BLOB sess_key = data_blob_null;
00219 secType_t sectype;
00220 key_serial_t key = 0;
00221 size_t datalen;
00222 long rc = 1;
00223 uid_t uid;
00224 int kernel_upcall_version;
00225 int c, use_cifs_service_prefix = 0;
00226 char *buf, *hostname = NULL;
00227 const char *oid;
00228
00229 openlog(prog, 0, LOG_DAEMON);
00230
00231 while ((c = getopt(argc, argv, "cv")) != -1) {
00232 switch (c) {
00233 case 'c':{
00234 use_cifs_service_prefix = 1;
00235 break;
00236 }
00237 case 'v':{
00238 printf("version: %s\n", CIFSSPNEGO_VERSION);
00239 goto out;
00240 }
00241 default:{
00242 syslog(LOG_WARNING, "unknown option: %c", c);
00243 goto out;
00244 }
00245 }
00246 }
00247
00248
00249 if (argc <= optind) {
00250 usage();
00251 goto out;
00252 }
00253
00254
00255 errno = 0;
00256 key = strtol(argv[optind], NULL, 10);
00257 if (errno != 0) {
00258 key = 0;
00259 syslog(LOG_WARNING, "Invalid key format: %s", strerror(errno));
00260 goto out;
00261 }
00262
00263 rc = keyctl_describe_alloc(key, &buf);
00264 if (rc == -1) {
00265 syslog(LOG_WARNING, "keyctl_describe_alloc failed: %s",
00266 strerror(errno));
00267 rc = 1;
00268 goto out;
00269 }
00270
00271 if ((strncmp(buf, "cifs.resolver", sizeof("cifs.resolver")-1) == 0) ||
00272 (strncmp(buf, "dns_resolver", sizeof("dns_resolver")-1) == 0)) {
00273 rc = cifs_resolver(key, buf);
00274 goto out;
00275 }
00276
00277 rc = decode_key_description(buf, &kernel_upcall_version, §ype,
00278 &hostname, &uid);
00279 if ((rc & DKD_MUSTHAVE_SET) != DKD_MUSTHAVE_SET) {
00280 syslog(LOG_WARNING,
00281 "unable to get from description necessary params");
00282 rc = 1;
00283 SAFE_FREE(buf);
00284 goto out;
00285 }
00286 SAFE_FREE(buf);
00287
00288 if (kernel_upcall_version > CIFS_SPNEGO_UPCALL_VERSION) {
00289 syslog(LOG_WARNING,
00290 "incompatible kernel upcall version: 0x%x",
00291 kernel_upcall_version);
00292 rc = 1;
00293 goto out;
00294 }
00295
00296 if (rc & DKD_HAVE_UID) {
00297 rc = setuid(uid);
00298 if (rc == -1) {
00299 syslog(LOG_WARNING, "setuid: %s", strerror(errno));
00300 goto out;
00301 }
00302 }
00303
00304
00305
00306
00307
00308 switch (sectype) {
00309 case MS_KRB5:
00310 case KRB5:{
00311 char *princ;
00312 size_t len;
00313
00314
00315 len = strlen(hostname) + 5 + 1;
00316 princ = SMB_XMALLOC_ARRAY(char, len);
00317 if (!princ) {
00318 rc = 1;
00319 break;
00320 }
00321 if (use_cifs_service_prefix) {
00322 strlcpy(princ, "cifs/", len);
00323 } else {
00324 strlcpy(princ, "host/", len);
00325 }
00326 strlcpy(princ + 5, hostname, len - 5);
00327
00328 if (sectype == MS_KRB5)
00329 oid = OID_KERBEROS5_OLD;
00330 else
00331 oid = OID_KERBEROS5;
00332
00333 rc = handle_krb5_mech(oid, princ, &secblob, &sess_key);
00334 SAFE_FREE(princ);
00335 break;
00336 }
00337 default:{
00338 syslog(LOG_WARNING, "sectype: %d is not implemented",
00339 sectype);
00340 rc = 1;
00341 break;
00342 }
00343 }
00344
00345 if (rc) {
00346 goto out;
00347 }
00348
00349
00350 datalen =
00351 sizeof(struct cifs_spnego_msg) + secblob.length + sess_key.length;
00352 keydata = (struct cifs_spnego_msg*)SMB_XMALLOC_ARRAY(char, datalen);
00353 if (!keydata) {
00354 rc = 1;
00355 goto out;
00356 }
00357 keydata->version = kernel_upcall_version;
00358 keydata->flags = 0;
00359 keydata->sesskey_len = sess_key.length;
00360 keydata->secblob_len = secblob.length;
00361 memcpy(&(keydata->data), sess_key.data, sess_key.length);
00362 memcpy(&(keydata->data) + keydata->sesskey_len,
00363 secblob.data, secblob.length);
00364
00365
00366 rc = keyctl_instantiate(key, keydata, datalen, 0);
00367 if (rc == -1) {
00368 syslog(LOG_WARNING, "keyctl_instantiate: %s", strerror(errno));
00369 goto out;
00370 }
00371
00372
00373
00374
00375 out:
00376
00377
00378
00379
00380
00381 if (rc != 0 && key == 0)
00382 keyctl_negate(key, 1, KEY_REQKEY_DEFL_DEFAULT);
00383 data_blob_free(&secblob);
00384 data_blob_free(&sess_key);
00385 SAFE_FREE(hostname);
00386 SAFE_FREE(keydata);
00387 return rc;
00388 }