|
30 | 30 | #include <pwd.h> |
31 | 31 | #include <string.h> |
32 | 32 |
|
| 33 | +#include "exception.h" |
33 | 34 | #include "org_apache_hadoop_security_JniBasedUnixGroupsMapping.h" |
34 | 35 | #include "org_apache_hadoop.h" |
| 36 | +#include "hadoop_group_info.h" |
| 37 | +#include "hadoop_user_info.h" |
35 | 38 |
|
36 | | -static jobjectArray emptyGroups = NULL; |
| 39 | +static jmethodID g_log_error_method; |
37 | 40 |
|
38 | | -JNIEXPORT jobjectArray JNICALL |
39 | | -Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_getGroupForUser |
40 | | -(JNIEnv *env, jobject jobj, jstring juser) { |
41 | | - extern int getGroupIDList(const char *user, int *ngroups, gid_t **groups); |
42 | | - extern int getGroupDetails(gid_t group, char **grpBuf); |
43 | | - const char *cuser = NULL; |
44 | | - jobjectArray jgroups = NULL; |
45 | | - int error = -1; |
| 41 | +static jclass g_string_clazz; |
46 | 42 |
|
47 | | - if (emptyGroups == NULL) { |
48 | | - jobjectArray lEmptyGroups = (jobjectArray)(*env)->NewObjectArray(env, 0, |
49 | | - (*env)->FindClass(env, "java/lang/String"), NULL); |
50 | | - if (lEmptyGroups == NULL) { |
51 | | - goto cleanup; |
52 | | - } |
53 | | - emptyGroups = (*env)->NewGlobalRef(env, lEmptyGroups); |
54 | | - if (emptyGroups == NULL) { |
55 | | - goto cleanup; |
56 | | - } |
| 43 | +extern jobject pw_lock_object; |
| 44 | + |
| 45 | +JNIEXPORT void JNICALL |
| 46 | +Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_anchorNative( |
| 47 | +JNIEnv *env, jclass clazz) |
| 48 | +{ |
| 49 | + jobject string_clazz; |
| 50 | + |
| 51 | + g_log_error_method = (*env)->GetStaticMethodID(env, clazz, "logError", |
| 52 | + "(ILjava/lang/String;)V"); |
| 53 | + if (!g_log_error_method) { |
| 54 | + return; // an exception has been raised |
57 | 55 | } |
58 | | - char *grpBuf = NULL; |
59 | | - cuser = (*env)->GetStringUTFChars(env, juser, NULL); |
60 | | - if (cuser == NULL) { |
61 | | - goto cleanup; |
| 56 | + string_clazz = (*env)->FindClass(env, "java/lang/String"); |
| 57 | + if (!string_clazz) { |
| 58 | + return; // an exception has been raised |
62 | 59 | } |
63 | | - |
64 | | - /*Get the number of the groups, and their IDs, this user belongs to*/ |
65 | | - gid_t *groups = NULL; |
66 | | - int ngroups = 0; |
67 | | - error = getGroupIDList(cuser, &ngroups, &groups); |
68 | | - if (error != 0) { |
69 | | - goto cleanup; |
| 60 | + g_string_clazz = (*env)->NewGlobalRef(env, string_clazz); |
| 61 | + if (!g_string_clazz) { |
| 62 | + jthrowable jthr = newRuntimeException(env, |
| 63 | + "JniBasedUnixGroupsMapping#anchorNative: failed to make " |
| 64 | + "a global reference to the java.lang.String class\n"); |
| 65 | + (*env)->Throw(env, jthr); |
| 66 | + return; |
70 | 67 | } |
| 68 | +} |
71 | 69 |
|
72 | | - jgroups = (jobjectArray)(*env)->NewObjectArray(env, ngroups, |
73 | | - (*env)->FindClass(env, "java/lang/String"), NULL); |
74 | | - if (jgroups == NULL) { |
75 | | - error = -1; |
76 | | - goto cleanup; |
| 70 | +/** |
| 71 | + * Log an error about a failure to look up a group ID |
| 72 | + * |
| 73 | + * @param env The JNI environment |
| 74 | + * @param clazz JniBasedUnixGroupsMapping class |
| 75 | + * @param gid The gid we failed to look up |
| 76 | + * @param ret Failure code |
| 77 | + */ |
| 78 | +static void logError(JNIEnv *env, jclass clazz, jint gid, int ret) |
| 79 | +{ |
| 80 | + jstring error_msg; |
| 81 | + |
| 82 | + error_msg = (*env)->NewStringUTF(env, terror(ret)); |
| 83 | + if (!error_msg) { |
| 84 | + (*env)->ExceptionClear(env); |
| 85 | + return; |
| 86 | + } |
| 87 | + (*env)->CallStaticVoidMethod(env, clazz, g_log_error_method, gid, error_msg); |
| 88 | + if ((*env)->ExceptionCheck(env)) { |
| 89 | + (*env)->ExceptionClear(env); |
| 90 | + return; |
77 | 91 | } |
| 92 | + (*env)->DeleteLocalRef(env, error_msg); |
| 93 | +} |
78 | 94 |
|
79 | | - /*Iterate over the groupIDs and get the group structure for each*/ |
80 | | - int i = 0; |
81 | | - for (i = 0; i < ngroups; i++) { |
82 | | - error = getGroupDetails(groups[i],&grpBuf); |
83 | | - if (error != 0) { |
84 | | - goto cleanup; |
85 | | - } |
86 | | - jstring jgrp = (*env)->NewStringUTF(env, ((struct group*)grpBuf)->gr_name); |
87 | | - if (jgrp == NULL) { |
88 | | - error = -1; |
89 | | - goto cleanup; |
| 95 | +JNIEXPORT jobjectArray JNICALL |
| 96 | +Java_org_apache_hadoop_security_JniBasedUnixGroupsMapping_getGroupsForUser |
| 97 | +(JNIEnv *env, jclass clazz, jstring jusername) |
| 98 | +{ |
| 99 | + const char *username = NULL; |
| 100 | + struct hadoop_user_info *uinfo = NULL; |
| 101 | + struct hadoop_group_info *ginfo = NULL; |
| 102 | + jstring jgroupname = NULL; |
| 103 | + int i, ret, nvalid; |
| 104 | + int pw_lock_locked = 0; |
| 105 | + jobjectArray jgroups = NULL, jnewgroups = NULL; |
| 106 | + |
| 107 | + if (pw_lock_object != NULL) { |
| 108 | + if ((*env)->MonitorEnter(env, pw_lock_object) != JNI_OK) { |
| 109 | + goto done; // exception thrown |
90 | 110 | } |
91 | | - (*env)->SetObjectArrayElement(env, jgroups,i,jgrp); |
92 | | - free(grpBuf); |
93 | | - grpBuf = NULL; |
| 111 | + pw_lock_locked = 1; |
94 | 112 | } |
95 | | - |
96 | | -cleanup: |
97 | | - if (error == ENOMEM) { |
| 113 | + username = (*env)->GetStringUTFChars(env, jusername, NULL); |
| 114 | + if (username == NULL) { |
| 115 | + goto done; // exception thrown |
| 116 | + } |
| 117 | + uinfo = hadoop_user_info_alloc(); |
| 118 | + if (!uinfo) { |
98 | 119 | THROW(env, "java/lang/OutOfMemoryError", NULL); |
| 120 | + goto done; |
| 121 | + } |
| 122 | + ret = hadoop_user_info_fetch(uinfo, username); |
| 123 | + if (ret == ENOENT) { |
| 124 | + jgroups = (*env)->NewObjectArray(env, 0, g_string_clazz, NULL); |
| 125 | + goto done; |
| 126 | + } |
| 127 | + ginfo = hadoop_group_info_alloc(); |
| 128 | + if (!ginfo) { |
| 129 | + THROW(env, "java/lang/OutOfMemoryError", NULL); |
| 130 | + goto done; |
| 131 | + } |
| 132 | + ret = hadoop_user_info_getgroups(uinfo); |
| 133 | + if (ret) { |
| 134 | + if (ret == ENOMEM) { |
| 135 | + THROW(env, "java/lang/OutOfMemoryError", NULL); |
| 136 | + } else { |
| 137 | + char buf[128]; |
| 138 | + snprintf(buf, sizeof(buf), "getgrouplist error %d (%s)", |
| 139 | + ret, terror(ret)); |
| 140 | + THROW(env, "java/lang/RuntimeException", buf); |
| 141 | + } |
| 142 | + goto done; |
| 143 | + } |
| 144 | + jgroups = (jobjectArray)(*env)->NewObjectArray(env, uinfo->num_gids, |
| 145 | + g_string_clazz, NULL); |
| 146 | + for (nvalid = 0, i = 0; i < uinfo->num_gids; i++) { |
| 147 | + ret = hadoop_group_info_fetch(ginfo, uinfo->gids[i]); |
| 148 | + if (ret) { |
| 149 | + logError(env, clazz, uinfo->gids[i], ret); |
| 150 | + } else { |
| 151 | + jgroupname = (*env)->NewStringUTF(env, ginfo->group.gr_name); |
| 152 | + if (!jgroupname) { // exception raised |
| 153 | + (*env)->DeleteLocalRef(env, jgroups); |
| 154 | + jgroups = NULL; |
| 155 | + goto done; |
| 156 | + } |
| 157 | + (*env)->SetObjectArrayElement(env, jgroups, nvalid++, jgroupname); |
| 158 | + // We delete the local reference once the element is in the array. |
| 159 | + // This is OK because the array has a reference to it. |
| 160 | + // Technically JNI only mandates that the JVM allow up to 16 local |
| 161 | + // references at a time (though many JVMs allow more than that.) |
| 162 | + (*env)->DeleteLocalRef(env, jgroupname); |
| 163 | + } |
| 164 | + } |
| 165 | + if (nvalid != uinfo->num_gids) { |
| 166 | + // If some group names could not be looked up, allocate a smaller array |
| 167 | + // with just the entries that could be resolved. Java has no equivalent to |
| 168 | + // realloc, so we have to do this manually. |
| 169 | + jnewgroups = (jobjectArray)(*env)->NewObjectArray(env, nvalid, |
| 170 | + (*env)->FindClass(env, "java/lang/String"), NULL); |
| 171 | + if (!jnewgroups) { // exception raised |
| 172 | + (*env)->DeleteLocalRef(env, jgroups); |
| 173 | + jgroups = NULL; |
| 174 | + goto done; |
| 175 | + } |
| 176 | + for (i = 0; i < nvalid; i++) { |
| 177 | + jgroupname = (*env)->GetObjectArrayElement(env, jgroups, i); |
| 178 | + (*env)->SetObjectArrayElement(env, jnewgroups, i, jgroupname); |
| 179 | + (*env)->DeleteLocalRef(env, jgroupname); |
| 180 | + } |
| 181 | + (*env)->DeleteLocalRef(env, jgroups); |
| 182 | + jgroups = jnewgroups; |
99 | 183 | } |
100 | | - if (error == ENOENT) { |
101 | | - THROW(env, "java/io/IOException", "No entry for user"); |
| 184 | + |
| 185 | +done: |
| 186 | + if (pw_lock_locked) { |
| 187 | + (*env)->MonitorExit(env, pw_lock_object); |
102 | 188 | } |
103 | | - if (groups != NULL) { |
104 | | - free(groups); |
| 189 | + if (username) { |
| 190 | + (*env)->ReleaseStringUTFChars(env, jusername, username); |
105 | 191 | } |
106 | | - if (grpBuf != NULL) { |
107 | | - free(grpBuf); |
| 192 | + if (uinfo) { |
| 193 | + hadoop_user_info_free(uinfo); |
108 | 194 | } |
109 | | - if (cuser != NULL) { |
110 | | - (*env)->ReleaseStringUTFChars(env, juser, cuser); |
| 195 | + if (ginfo) { |
| 196 | + hadoop_group_info_free(ginfo); |
111 | 197 | } |
112 | | - if (error == 0) { |
113 | | - return jgroups; |
114 | | - } else { |
115 | | - return emptyGroups; |
| 198 | + if (jgroupname) { |
| 199 | + (*env)->DeleteLocalRef(env, jgroupname); |
116 | 200 | } |
| 201 | + return jgroups; |
117 | 202 | } |
0 commit comments